1
# ------------------------------------------------------------------------------
2
#
3
#  Copyright (c) 2005, Enthought, Inc.
4
#  All rights reserved.
5
#
6
#  This software is provided without warranty under the terms of the BSD
7
#  license included in LICENSE.txt and may be redistributed only
8
#  under the conditions described in the aforementioned license.  The license
9
#  is also available online at http://www.enthought.com/licenses/BSD.txt
10
#
11
#  Thanks for using Enthought open source!
12
#
13
#  Author: David C. Morrill
14
#  Date:   10/21/2004
15
#
16
# ------------------------------------------------------------------------------
17

18 0
""" Defines the various list editors for the wxPython user interface toolkit.
19
"""
20

21

22 0
import wx
23

24 0
import wx.lib.scrolledpanel as wxsp
25

26 0
from traits.api import Str, Any, Instance, Property, Bool, cached_property
27 0
from traits.trait_base import user_name_for, xgetattr
28

29 0
from traitsui.ui_traits import Image, convert_bitmap
30 0
from traitsui.editors.list_editor import ListItemProxy, ToolkitEditorFactory
31 0
from traitsui.dockable_view_element import DockableViewElement
32

33 0
from pyface.dock.api import (
34
    DockWindow,
35
    DockSizer,
36
    DockSection,
37
    DockRegion,
38
    DockControl,
39
)
40

41 0
from . import toolkit
42 0
from .constants import scrollbar_dx
43 0
from .editor import Editor
44 0
from .menu import MakeMenu
45 0
from .image_control import ImageControl
46

47

48

49
# -------------------------------------------------------------------------
50
#  'SimpleEditor' class:
51
# -------------------------------------------------------------------------
52

53

54 0
class SimpleEditor(Editor):
55
    """ Simple style of editor for lists, which displays a scrolling list box
56
    with only one item visible at a time. A icon next to the list box displays
57
    a menu of operations on the list.
58
    """
59

60
    # -------------------------------------------------------------------------
61
    #  Trait definitions:
62
    # -------------------------------------------------------------------------
63

64
    #: The kind of editor to create for each list item
65 0
    kind = Str()
66

67
    #: Is the list of items being edited mutable?
68 0
    mutable = Bool()
69

70
    #: The image used by the editor:
71 0
    image = Image("list_editor")
72

73
    #: The bitmap used by the editor:
74 0
    bitmap = Property()
75

76
    # -------------------------------------------------------------------------
77
    #  Class constants:
78
    # -------------------------------------------------------------------------
79

80
    #: Whether the list is displayed in a single row
81 0
    single_row = True
82

83
    # -------------------------------------------------------------------------
84
    #  Normal list item menu:
85
    # -------------------------------------------------------------------------
86

87
    #: Menu for modifying the list
88 0
    list_menu = """
89
       Add Before     [_menu_before]: self.add_before()
90
       Add After      [_menu_after]:  self.add_after()
91
       ---
92
       Delete         [_menu_delete]: self.delete_item()
93
       ---
94
       Move Up        [_menu_up]:     self.move_up()
95
       Move Down      [_menu_down]:   self.move_down()
96
       Move to Top    [_menu_top]:    self.move_top()
97
       Move to Bottom [_menu_bottom]: self.move_bottom()
98
    """
99

100
    # -------------------------------------------------------------------------
101
    #  Empty list item menu:
102
    # -------------------------------------------------------------------------
103

104 0
    empty_list_menu = """
105
       Add: self.add_empty()
106
    """
107

108 0
    def init(self, parent):
109
        """ Finishes initializing the editor by creating the underlying toolkit
110
            widget.
111
        """
112
        # Initialize the trait handler to use:
113 0
        trait_handler = self.factory.trait_handler
114 11
        if trait_handler is None:
115 0
            trait_handler = self.object.base_trait(self.name).handler
116 0
        self._trait_handler = trait_handler
117

118
        # Create a scrolled window to hold all of the list item controls:
119 0
        self.control = wxsp.ScrolledPanel(parent, -1)
120 0
        self.control.SetBackgroundColour(parent.GetBackgroundColour())
121 0
        self.control.SetAutoLayout(True)
122

123
        # Remember the editor to use for each individual list item:
124 0
        editor = self.factory.editor
125 11
        if editor is None:
126 0
            editor = trait_handler.item_trait.get_editor()
127 0
        self._editor = getattr(editor, self.kind)
128

129
        # Set up the additional 'list items changed' event handler needed for
130
        # a list based trait. Note that we want to fire the update_editor_item
131
        # only when the items in the list change and not when intermediate
132
        # traits change. Therefore, replace "." by ":" in the extended_name
133
        # when setting up the listener.
134 0
        extended_name = self.extended_name.replace(".", ":")
135 0
        self.context_object.on_trait_change(
136
            self.update_editor_item, extended_name + "_items?", dispatch="ui"
137
        )
138 0
        self.set_tooltip()
139

140 0
    def dispose(self):
141
        """ Disposes of the contents of an editor.
142
        """
143 0
        extended_name = self.extended_name.replace(".", ":")
144 0
        self.context_object.on_trait_change(
145
            self.update_editor_item, extended_name + "_items?", remove=True
146
        )
147 0
        self._dispose_items()
148

149 0
        super(SimpleEditor, self).dispose()
150

151 0
    def update_editor(self):
152
        """ Updates the editor when the object trait changes externally to the
153
            editor.
154
        """
155
        # Disconnect the editor from any control about to be destroyed:
156 0
        self._dispose_items()
157

158
        # Get rid of any previous contents:
159 0
        list_pane = self.control
160 0
        list_pane.SetSizer(None)
161 11
        for child in list_pane.GetChildren():
162 0
            toolkit.destroy_control(child)
163

164
        # Create all of the list item trait editors:
165 0
        trait_handler = self._trait_handler
166 0
        resizable = (
167
            trait_handler.minlen != trait_handler.maxlen
168
        ) and self.mutable
169 0
        item_trait = trait_handler.item_trait
170 0
        factory = self.factory
171 0
        list_sizer = wx.FlexGridSizer(
172
            len(self.value), (1 + resizable) * factory.columns, 0, 0
173
        )
174 0
        j = 0
175 11
        for i in range(factory.columns):
176 0
            list_sizer.AddGrowableCol(j)
177 0
            j += 1 + resizable
178

179 0
        values = self.value
180 0
        index = 0
181 0
        width, height = 0, 0
182

183 0
        is_fake = resizable and (values is None or len(values) == 0)
184 11
        if is_fake:
185 0
            values = [item_trait.default_value()[1]]
186

187 0
        panel_height = 0
188 0
        editor = self._editor
189 11
        for value in values:
190 0
            width1 = height = 0
191 11
            if resizable:
192 0
                control = ImageControl(
193
                    list_pane, self.bitmap, -1, self.popup_menu
194
                )
195 0
                width1, height = control.GetSize()
196 0
                width1 += 4
197

198 0
            try:
199 0
                proxy = ListItemProxy(
200
                    self.object, self.name, index, item_trait, value
201
                )
202 11
                if resizable:
203 0
                    control.proxy = proxy
204 0
                peditor = editor(
205
                    self.ui, proxy, "value", self.description, list_pane
206
                ).trait_set(object_name="")
207 0
                peditor.prepare(list_pane)
208 0
                pcontrol = peditor.control
209 0
                pcontrol.proxy = proxy
210 0
            except:
211 11
                if not is_fake:
212 0
                    raise
213

214 0
                pcontrol = wx.Button(list_pane, -1, "sample")
215

216 0
            pcontrol.Fit()
217 0
            width2, height2 = size = pcontrol.GetSize()
218 0
            pcontrol.SetMinSize(size)
219 0
            width = max(width, width1 + width2)
220 0
            height = max(height, height2)
221 0
            panel_height += height
222

223 0
            list_sizer.Add(pcontrol, 0, wx.EXPAND)
224

225 11
            if resizable:
226 0
                list_sizer.Add(control, 0, wx.LEFT | wx.RIGHT, 2)
227

228 0
            index += 1
229

230 0
        list_pane.SetSizer(list_sizer)
231

232 11
        if not self.mutable:
233
            # list_sizer.SetDimension(0,0,width, panel_height)
234 0
            list_pane.SetInitialSize(list_sizer.GetSize())
235

236 11
        if is_fake:
237 0
            self._cur_control = control
238 0
            self.empty_list()
239 0
            control.Destroy()
240 0
            pcontrol.Destroy()
241

242 0
        rows = 1
243 11
        if not self.single_row:
244 0
            rows = self.factory.rows
245

246
        # Make sure we have valid values set for width and height (in case there
247
        # was no data to base them on):
248 11
        if width == 0:
249 0
            width = 100
250

251 11
        if panel_height == 0:
252 0
            panel_height = 20
253

254 0
        list_pane.SetMinSize(
255
            wx.Size(
256
                width + ((trait_handler.maxlen > rows) * scrollbar_dx),
257
                panel_height,
258
            )
259
        )
260

261 0
        list_pane.SetupScrolling()
262 0
        list_pane.GetParent().Layout()
263

264 0
    def update_editor_item(self, obj, name, event):
265
        """ Updates the editor when an item in the object trait changes
266
        externally to the editor.
267
        """
268
        # If this is not a simple, single item update, rebuild entire editor:
269 11
        if (len(event.removed) != 1) or (len(event.added) != 1):
270 0
            self.update_editor()
271 0
            return
272

273
        # Otherwise, find the proxy for this index and update it with the
274
        # changed value:
275 11
        for control in self.control.GetChildren():
276 0
            proxy = control.proxy
277 11
            if proxy.index == event.index:
278 0
                proxy.value = event.added[0]
279 0
                break
280

281 0
    def empty_list(self):
282
        """ Creates an empty list entry (so the user can add a new item).
283
        """
284 0
        control = ImageControl(
285
            self.control, self.bitmap, -1, self.popup_empty_menu
286
        )
287 0
        control.is_empty = True
288 0
        proxy = ListItemProxy(self.object, self.name, -1, None, None)
289 0
        pcontrol = wx.StaticText(self.control, -1, "   (Empty List)")
290 0
        pcontrol.proxy = control.proxy = proxy
291 0
        self.reload_sizer([(control, pcontrol)])
292

293 0
    def reload_sizer(self, controls, extra=0):
294
        """ Reloads the layout from the specified list of ( button, proxy )
295
            pairs.
296
        """
297 0
        sizer = self.control.GetSizer()
298 11
        for i in range(2 * len(controls) + extra):
299 0
            sizer.Remove(0)
300 0
        index = 0
301 11
        for control, pcontrol in controls:
302 0
            sizer.Add(pcontrol, 1, wx.EXPAND)
303 0
            sizer.Add(control, 0, wx.LEFT | wx.RIGHT, 2)
304 0
            control.proxy.index = index
305 0
            index += 1
306 0
        sizer.Layout()
307 0
        self.control.SetVirtualSize(sizer.GetMinSize())
308

309 0
    def get_info(self):
310
        """ Returns the associated object list and current item index.
311
        """
312 0
        proxy = self._cur_control.proxy
313 0
        return (proxy.list, proxy.index)
314

315 0
    def popup_empty_menu(self, control):
316
        """ Displays the empty list editor popup menu.
317
        """
318 0
        self._cur_control = control
319 0
        menu = MakeMenu(self.empty_list_menu, self, True, self.control).menu
320 0
        self.control.PopupMenu(menu, control.GetPosition())
321 0
        menu.Destroy()
322

323 0
    def popup_menu(self, control):
324
        """ Displays the list editor popup menu.
325
        """
326 0
        self._cur_control = control
327
        # Makes sure that any text that was entered get's added (Pressure
328
        # #145):
329 0
        control.SetFocus()
330 0
        proxy = control.proxy
331 0
        index = proxy.index
332 0
        menu = MakeMenu(self.list_menu, self, True, self.control).menu
333 0
        len_list = len(proxy.list)
334 0
        not_full = len_list < self._trait_handler.maxlen
335 0
        self._menu_before.enabled(not_full)
336 0
        self._menu_after.enabled(not_full)
337 0
        self._menu_delete.enabled(len_list > self._trait_handler.minlen)
338 0
        self._menu_up.enabled(index > 0)
339 0
        self._menu_top.enabled(index > 0)
340 0
        self._menu_down.enabled(index < (len_list - 1))
341 0
        self._menu_bottom.enabled(index < (len_list - 1))
342 0
        x, y = control.GetPosition()
343

344 0
        self.control.PopupMenu(menu, (x + 8, y + 32))
345 0
        menu.Destroy()
346

347 0
    def add_item(self, offset):
348
        """ Adds a new value at the specified list index.
349
        """
350 0
        list, index = self.get_info()
351 0
        index += offset
352 0
        item_trait = self._trait_handler.item_trait
353 0
        value = item_trait.default_value_for(self.object, self.name)
354 0
        self.value = list[:index] + [value] + list[index:]
355 0
        wx.CallAfter(self.update_editor)
356

357 0
    def add_before(self):
358
        """ Inserts a new item before the current item.
359
        """
360 0
        self.add_item(0)
361

362 0
    def add_after(self):
363
        """ Inserts a new item after the current item.
364
        """
365 0
        self.add_item(1)
366

367 0
    def add_empty(self):
368
        """ Adds a new item when the list is empty.
369
        """
370 0
        list, index = self.get_info()
371 0
        self.add_item(0)
372

373 0
    def delete_item(self):
374
        """ Delete the current item.
375
        """
376 0
        list, index = self.get_info()
377 0
        self.value = list[:index] + list[index + 1 :]
378 0
        wx.CallAfter(self.update_editor)
379

380 0
    def move_up(self):
381
        """ Move the current item up one in the list.
382
        """
383 0
        list, index = self.get_info()
384 0
        self.value = (
385
            list[: index - 1]
386
            + [list[index], list[index - 1]]
387
            + list[index + 1 :]
388
        )
389 0
        wx.CallAfter(self.update_editor)
390

391 0
    def move_down(self):
392
        """ Moves the current item down one in the list.
393
        """
394 0
        list, index = self.get_info()
395 0
        self.value = (
396
            list[:index] + [list[index + 1], list[index]] + list[index + 2 :]
397
        )
398 0
        wx.CallAfter(self.update_editor)
399

400 0
    def move_top(self):
401
        """ Moves the current item to the top of the list.
402
        """
403 0
        list, index = self.get_info()
404 0
        self.value = [list[index]] + list[:index] + list[index + 1 :]
405 0
        wx.CallAfter(self.update_editor)
406

407 0
    def move_bottom(self):
408
        """ Moves the current item to the bottom of the list.
409
        """
410 0
        list, index = self.get_info()
411 0
        self.value = list[:index] + list[index + 1 :] + [list[index]]
412 0
        wx.CallAfter(self.update_editor)
413

414
    # -- Property Implementations ---------------------------------------------
415

416 0
    @cached_property
417
    def _get_bitmap(self):
418 0
        return convert_bitmap(self.image)
419

420
    # -- Private Methods ------------------------------------------------------
421

422 0
    def _dispose_items(self):
423
        """ Disposes of each current list item.
424
        """
425 11
        for control in self.control.GetChildren():
426 0
            editor = getattr(control, "_editor", None)
427 11
            if editor is not None:
428 0
                try:
429 0
                    editor.dispose()
430 0
                except Exception:
431 0
                    pass
432 0
                editor.control = None
433

434
    # -- Trait initializers ----------------------------------------------------
435

436 0
    def _kind_default(self):
437
        """ Returns a default value for the 'kind' trait.
438
        """
439 0
        return self.factory.style + "_editor"
440

441 0
    def _mutable_default(self):
442
        """ Trait handler to set the mutable trait from the factory. """
443 0
        return self.factory.mutable
444

445

446 0
class CustomEditor(SimpleEditor):
447
    """ Custom style of editor for lists, which displays the items as a series
448
    of text fields. If the list is editable, an icon next to each item displays
449
    a menu of operations on the list.
450
    """
451

452
    # -------------------------------------------------------------------------
453
    #  Class constants:
454
    # -------------------------------------------------------------------------
455

456
    #: Whether the list is displayed in a single row. This value overrides the
457
    #: default.
458 0
    single_row = False
459

460
    # -------------------------------------------------------------------------
461
    #  Trait definitions:
462
    # -------------------------------------------------------------------------
463

464
    #: Is the list editor is scrollable? This values overrides the default.
465 0
    scrollable = True
466

467

468
# -------------------------------------------------------------------------
469
#  'TextEditor' class:
470
# -------------------------------------------------------------------------
471

472

473 0
class TextEditor(CustomEditor):
474

475
    #: The kind of editor to create for each list item. This value overrides the
476
    #: default.
477 0
    kind = "text_editor"
478

479

480 0
class ReadonlyEditor(CustomEditor):
481

482
    #: Is the list of items being edited mutable? This value overrides the
483
    #: default.
484 0
    mutable = False
485

486

487 0
class NotebookEditor(Editor):
488
    """ An editor for lists that displays the list as a "notebook" of tabbed
489
        pages.
490
    """
491

492
    # -------------------------------------------------------------------------
493
    #  Trait definitions:
494
    # -------------------------------------------------------------------------
495

496
    #: Is the notebook editor scrollable? This values overrides the default:
497 0
    scrollable = True
498

499
    #: The currently selected notebook page object:
500 0
    selected = Any()
501

502 0
    def init(self, parent):
503
        """ Finishes initializing the editor by creating the underlying toolkit
504
            widget.
505
        """
506 0
        self._uis = []
507

508
        # Create a DockWindow to hold each separate object's view:
509 0
        theme = self.factory.dock_theme or self.item.container.dock_theme
510 0
        dw = DockWindow(parent, theme=theme)
511 0
        self.control = dw.control
512 0
        self._sizer = DockSizer(DockSection(dock_window=dw))
513 0
        self.control.SetSizer(self._sizer)
514

515
        # Set up the additional 'list items changed' event handler needed for
516
        # a list based trait. Note that we want to fire the update_editor_item
517
        # only when the items in the list change and not when intermediate
518
        # traits change. Therefore, replace "." by ":" in the extended_name
519
        # when setting up the listener.
520 0
        extended_name = self.extended_name.replace(".", ":")
521 0
        self.context_object.on_trait_change(
522
            self.update_editor_item, extended_name + "_items?", dispatch="ui"
523
        )
524

525
        # Set of selection synchronization:
526 0
        self.sync_value(self.factory.selected, "selected")
527

528 0
    def update_editor(self):
529
        """ Updates the editor when the object trait changes externally to the
530
            editor.
531
        """
532
        # Make sure the DockWindow is in a correct state:
533 0
        self._sizer.Reset(self.control)
534

535
        # Destroy the views on each current notebook page:
536 0
        self.close_all()
537

538
        # Create a DockControl for each object in the trait's value:
539 0
        uis = self._uis
540 0
        dock_controls = []
541 11
        for object in self.value:
542 0
            dock_control, view_object, monitoring = self._create_page(object)
543
            # Remember the DockControl for later deletion processing:
544 0
            uis.append([dock_control, object, view_object, monitoring])
545 0
            dock_controls.append(dock_control)
546

547
        # Add the new items to the DockWindow:
548 0
        self.add_controls(dock_controls)
549

550 11
        if self.ui.info.initialized:
551 0
            self.update_layout()
552

553 0
    def update_editor_item(self, event):
554
        """ Handles an update to some subset of the trait's list.
555
        """
556
        # Make sure the DockWindow is in a correct state:
557 0
        self._sizer.Reset(self.control)
558

559 0
        index = event.index
560

561
        # Delete the page corresponding to each removed item:
562 0
        layout = (len(event.removed) + len(event.added)) <= 1
563 11
        for i in range(len(event.removed)):
564 0
            dock_control, object, view_object, monitoring = self._uis[index]
565 11
            if monitoring:
566 0
                view_object.on_trait_change(
567
                    self.update_page_name,
568
                    self.factory.page_name[1:],
569
                    remove=True,
570
                )
571 0
            dock_control.close(layout=layout, force=True)
572 0
            del self._uis[index]
573

574
        # Add a page for each added object:
575 0
        dock_controls = []
576 11
        for object in event.added:
577 0
            dock_control, view_object, monitoring = self._create_page(object)
578 0
            self._uis[index:index] = [
579
                [dock_control, object, view_object, monitoring]
580
            ]
581 0
            dock_controls.append(dock_control)
582 0
            index += 1
583

584
        # Add the new items to the DockWindow:
585 0
        self.add_controls(dock_controls)
586

587 0
        self.update_layout()
588

589 0
    def close_all(self):
590
        """ Closes all currently open notebook pages.
591
        """
592 0
        page_name = self.factory.page_name[1:]
593 11
        for dock_control, object, view_object, monitoring in self._uis:
594 11
            if monitoring:
595 0
                view_object.on_trait_change(
596
                    self.update_page_name, page_name, remove=True
597
                )
598 0
            dock_control.close(layout=False, force=True)
599

600
        # Reset the list of ui's and dictionary of page name counts:
601 0
        self._uis = []
602 0
        self._pages = {}
603

604 0
    def dispose(self):
605
        """ Disposes of the contents of an editor.
606
        """
607 0
        self.context_object.on_trait_change(
608
            self.update_editor_item, self.name + "_items?", remove=True
609
        )
610 0
        self.close_all()
611

612 0
        super(NotebookEditor, self).dispose()
613

614 0
    def add_controls(self, controls):
615
        """ Adds a group of new DockControls to the view.
616
        """
617 11
        if len(controls) > 0:
618 0
            section = self.control.GetSizer().GetContents()
619 11
            if (len(section.contents) == 0) or (
620
                not isinstance(section.contents[-1], DockRegion)
621
            ):
622 0
                section.contents.append(DockRegion(contents=controls))
623
            else:
624 11
                for control in controls:
625 0
                    section.contents[-1].add(control, activate=False)
626
            # Fire this event to activate the dock control corresponding
627
            # to the selected object, if any.
628 0
            self._selected_changed(None, self.selected)
629

630 0
    def update_layout(self):
631
        """ Updates the layout of the DockWindow.
632
        """
633 0
        self.control.Layout()
634 0
        self.control.Refresh()
635

636 0
    def update_page_name(self):
637
        """ Handles the trait defining a particular page's name being changed.
638
        """
639 0
        changed = False
640 11
        for i, value in enumerate(self._uis):
641 0
            dock_control, user_object, view_object, monitoring = value
642 11
            if dock_control.control is not None:
643 0
                name = None
644 0
                handler = getattr(
645
                    self.ui.handler,
646
                    "%s_%s_page_name" % (self.object_name, self.name),
647
                    None,
648
                )
649 11
                if handler is not None:
650 0
                    name = handler(self.ui.info, user_object)
651

652 11
                if name is None:
653 0
                    name = str(
654
                        xgetattr(
655
                            view_object, self.factory.page_name[1:], "???"
656
                        )
657
                    )
658

659 0
                changed |= dock_control.name != name
660 0
                dock_control.name = name
661

662 11
        if changed:
663 0
            self.update_layout()
664

665 0
    def _create_page(self, object):
666
        """ Creates a DockControl for a specified object.
667
        """
668
        # Create the view for the object:
669 0
        view_object = object
670 0
        factory = self.factory
671 11
        if factory.factory is not None:
672 0
            view_object = factory.factory(object)
673

674 0
        ui = view_object.edit_traits(
675
            parent=self.control, view=factory.view, kind=factory.ui_kind
676
        ).trait_set(parent=self.ui)
677

678
        # Get the name of the page being added to the notebook:
679 0
        name = ""
680 0
        monitoring = False
681 0
        prefix = "%s_%s_page_" % (self.object_name, self.name)
682 0
        page_name = self.factory.page_name
683 11
        if page_name[0:1] == ".":
684 0
            name = xgetattr(view_object, page_name[1:], None)
685 0
            monitoring = name is not None
686 11
            if monitoring:
687 0
                handler_name = None
688 0
                method = getattr(self.ui.handler, prefix + "name", None)
689 11
                if method is not None:
690 0
                    handler_name = method(self.ui.info, object)
691 11
                if handler_name is not None:
692 0
                    name = handler_name
693
                else:
694 0
                    name = str(name) or "???"
695 0
                view_object.on_trait_change(
696
                    self.update_page_name, page_name[1:], dispatch="ui"
697
                )
698
            else:
699 0
                name = ""
700 11
        elif page_name != "":
701 0
            name = page_name
702

703 11
        if name == "":
704 0
            name = user_name_for(view_object.__class__.__name__)
705

706
        # Make sure the name is not a duplicate:
707 11
        if not monitoring:
708 0
            self._pages[name] = count = self._pages.get(name, 0) + 1
709 11
            if count > 1:
710 0
                name += " %d" % count
711

712
        # Return a new DockControl for the ui, and whether or not its name is
713
        # being monitored:
714 0
        image = None
715 0
        method = getattr(self.ui.handler, prefix + "image", None)
716 11
        if method is not None:
717 0
            image = method(self.ui.info, object)
718 0
        dock_control = DockControl(
719
            control=ui.control,
720
            id=str(id(ui.control)),
721
            name=name,
722
            style=factory.dock_style,
723
            image=image,
724
            export=factory.export,
725
            closeable=factory.deletable,
726
            dockable=DockableListElement(ui=ui, editor=self),
727
        )
728 0
        return (dock_control, view_object, monitoring)
729

730
    # -------------------------------------------------------------------------
731
    #  Activates the corresponding dock window when the 'selected' trait of
732
    #  the editor is changed.
733
    # -------------------------------------------------------------------------
734 0
    def _selected_changed(self, old, new):
735
        """ Activates the corresponding dock window when the 'selected' trait
736
        of the editor is changed.
737
        """
738 11
        for i, value in enumerate(self._uis):
739 11
            if new == value[1]:
740 0
                value[0].activate()
741 0
                break
742 0
        return
743

744

745 0
class DockableListElement(DockableViewElement):
746

747
    # -------------------------------------------------------------------------
748
    #  Trait definitions:
749
    # -------------------------------------------------------------------------
750

751
    #: The editor this dockable item is associated with:
752 0
    editor = Instance(NotebookEditor)
753

754 0
    def dockable_close(self, dock_control, force):
755
        """ Returns whether it is OK to close the control.
756
        """
757 0
        return self.close_dock_control(dock_control, force)
758

759 0
    def close_dock_control(self, dock_control, abort):
760
        """ Closes a DockControl.
761
        """
762 11
        if abort:
763 0
            return super(DockableListElement, self).close_dock_control(
764
                dock_control, False
765
            )
766

767 0
        view_object = self.ui.context["object"]
768 11
        for i, value in enumerate(self.editor._uis):
769 11
            if view_object is value[2]:
770 0
                del self.editor.value[i]
771

772 0
        return False
773

774 0
    def dockable_tab_activated(self, dock_control, activated):
775
        """ Handles a notebook tab being activated or deactivated.
776
        Sets the value of the editor's selected trait to the activated
777
        dock_control's object.
778

779
        """
780 11
        for i, value in enumerate(self.editor._uis):
781 11
            if dock_control is value[0] and activated:
782 0
                self.editor.selected = value[1]
783 0
                break

Read our documentation on viewing source code .

Loading