Example #1
0
class SafeImageListTest(unittest.TestCase, BaseOutWikerGUIMixin):
    def setUp(self):
        self.initApplication()
        self.width = 16
        self.height = 16
        self.imagelist = SafeImageList(self.width, self.height)

    def tearDown(self):
        self.destroyApplication()

    def _addImage(self, fname):
        bitmap = wx.Bitmap(fname)
        self.imagelist.Add(bitmap)

    def test_16x16(self):
        self._addImage('../test/images/16x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_15x15(self):
        self._addImage('../test/images/15x15.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_15x16(self):
        self._addImage('../test/images/15x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_16x15(self):
        self._addImage('../test/images/16x15.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_17x16(self):
        self._addImage('../test/images/17x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_16x17(self):
        self._addImage('../test/images/16x17.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_17x17(self):
        self._addImage('../test/images/17x17.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)
Example #2
0
class SafeImageListTest(unittest.TestCase):
    def setUp(self):
        self.width = 16
        self.height = 16
        self.imagelist = SafeImageList(self.width, self.height)

    def _addImage(self, fname):
        bitmap = wx.Bitmap(fname)
        self.imagelist.Add(bitmap)

    def test_16x16(self):
        self._addImage(u'../test/images/16x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_15x15(self):
        self._addImage(u'../test/images/15x15.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_15x16(self):
        self._addImage(u'../test/images/15x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_16x15(self):
        self._addImage(u'../test/images/16x15.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_17x16(self):
        self._addImage(u'../test/images/17x16.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_16x17(self):
        self._addImage(u'../test/images/16x17.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)

    def test_17x17(self):
        self._addImage(u'../test/images/17x17.png')
        size = self.imagelist.GetSize(0)
        self.assertEqual(size[0], self.width)
        self.assertEqual(size[1], self.height)
Example #3
0
class EditSnippetsDialog(TestedDialog):
    '''
    Dialog to create, edit and remove snippets and folders.
    '''
    def __init__(self, parent):
        super(EditSnippetsDialog,
              self).__init__(parent,
                             style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        global _
        _ = get_()

        self.ICON_WIDTH = 16
        self.ICON_HEIGHT = 16

        self._imagesPath = getImagesPath()
        self._dirImageId = None
        self._snippetImageId = None

        self.addGroupBtn = None
        self.addSnippetBtn = None

        self._varMenuItems = [
            (_(u'Selected text'), defines.VAR_SEL_TEXT),
            (_(u'Current date'), defines.VAR_DATE),
            (_(u'Page title'), defines.VAR_TITLE),
            (_(u'Page type'), defines.VAR_PAGE_TYPE),
            (_(u'Page tags list'), defines.VAR_TAGS),
            (_(u'Attachments path'), defines.VAR_ATTACH),
            (_(u'Path to page'), defines.VAR_FOLDER),
            (_(u'Relative page path'), defines.VAR_SUBPATH),
            (_(u'Page creation date'), defines.VAR_DATE_CREATING),
            (_(u'Page modification date'), defines.VAR_DATE_EDITIND),
            (_(u'Page Id'), defines.VAR_PAGE_ID),
            (_(u'Attachments list'), defines.VAR_ATTACHLIST),
            (_(u'Child pages'), defines.VAR_CHILDLIST),
        ]

        self._blocksMenuItems = [
            (_('{% if %}'), (u'{% if %}', '{% elif %}{% else %}{% endif %}')),
            (_('{% include %}'), (u"{% include '", u"' %}")),
            (_('{# comment #}'), (u'{# ', ' #}')),
        ]

        self._createGUI()
        self.SetTitle(_(u'Snippets management'))
        self.disableSnippetEditor()

    def disableSnippetEditor(self):
        self.snippetEditor.SetText(u'')
        self._snippetPanel.Disable()
        self.runSnippetBtn.Disable()

    def enableSnippetEditor(self):
        self._snippetPanel.Enable()
        self.runSnippetBtn.Enable()

    def appendDirTreeItem(self, parentItem, name, data):
        itemData = wx.TreeItemData(data)
        if parentItem is not None:
            newItemId = self.snippetsTree.AppendItem(parentItem,
                                                     name,
                                                     self._dirImageId,
                                                     data=itemData)
        else:
            newItemId = self.snippetsTree.AddRoot(name,
                                                  self._dirImageId,
                                                  data=itemData)
        return newItemId

    def appendSnippetTreeItem(self, parentItem, name, data):
        itemData = wx.TreeItemData(data)
        newItemId = self.snippetsTree.AppendItem(parentItem,
                                                 name,
                                                 self._snippetImageId,
                                                 data=itemData)
        return newItemId

    def _createTreeButtons(self, groupButtonsSizer):
        # Add a group button
        self.addGroupBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(self._imagesPath, "folder_add.png")))
        self.addGroupBtn.SetToolTipString(_(u"Add new snippets group"))
        groupButtonsSizer.Add(self.addGroupBtn, flag=wx.ALL, border=0)

        # Add a snippet button
        self.addSnippetBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(self._imagesPath,
                                          "snippet_add.png")))
        self.addSnippetBtn.SetToolTipString(_(u"Create new snippet"))
        groupButtonsSizer.Add(self.addSnippetBtn, flag=wx.ALL, border=0)

        # Rename group or snippet button
        self.renameBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(self._imagesPath, "rename.png")))
        self.renameBtn.SetToolTipString(_(u"Rename"))
        groupButtonsSizer.Add(self.renameBtn, flag=wx.ALL, border=0)

        # Remove group or snippet button
        self.removeBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(self._imagesPath, "remove.png")))
        self.removeBtn.SetToolTipString(_(u"Remove"))
        groupButtonsSizer.Add(self.removeBtn, flag=wx.ALL, border=0)

        # Run snippet
        self.runSnippetBtn = wx.BitmapButton(
            self, bitmap=wx.Bitmap(os.path.join(self._imagesPath, "run.png")))
        self.runSnippetBtn.SetToolTipString(_(u"Run snippet"))
        groupButtonsSizer.Add(self.runSnippetBtn, flag=wx.ALL, border=0)

        # Open help
        self.openHelpBtn = wx.BitmapButton(
            self, bitmap=wx.Bitmap(os.path.join(self._imagesPath, "help.png")))
        self.openHelpBtn.SetToolTipString(_(u"Open help..."))
        groupButtonsSizer.Add(self.openHelpBtn, flag=wx.ALL, border=0)

    def _createImagesList(self):
        self._imagelist = SafeImageList(self.ICON_WIDTH, self.ICON_HEIGHT)

        self._dirImageId = self._imagelist.Add(
            wx.Bitmap(os.path.join(self._imagesPath, u'folder.png')))

        self._snippetImageId = self._imagelist.Add(
            wx.Bitmap(os.path.join(self._imagesPath, u'snippet.png')))

    def _createTreePanel(self, mainSizer):
        self._createImagesList()

        self.snippetsTree = wx.TreeCtrl(self,
                                        style=wx.TR_HAS_BUTTONS
                                        | wx.TR_EDIT_LABELS | wx.SUNKEN_BORDER)
        self.snippetsTree.SetMinSize((200, 200))

        self.snippetsTree.AssignImageList(self._imagelist)

        # Buttons for the snippets tree
        groupButtonsSizer = wx.BoxSizer(wx.HORIZONTAL)
        self._createTreeButtons(groupButtonsSizer)

        # TreeSizer
        treeSizer = wx.FlexGridSizer(cols=1)
        treeSizer.AddGrowableRow(1)
        treeSizer.AddGrowableCol(0)
        treeSizer.Add(groupButtonsSizer, 1, wx.EXPAND, border=2)
        treeSizer.Add(self.snippetsTree, 1, wx.EXPAND, border=2)

        mainSizer.Add(treeSizer, 1, wx.ALL | wx.EXPAND, border=2)

    def _createSnippetButtons(self, snippetButtonsSizer, parent):
        # Insert variable
        self.insertVariableBtn = PopupButton(parent,
                                             bitmap=wx.Bitmap(
                                                 os.path.join(
                                                     self._imagesPath,
                                                     "variables-menu.png")))
        self.insertVariableBtn.SetToolTipString(_(u"Insert variable"))

        for menuitem in self._varMenuItems:
            data = u'{{' + menuitem[1] + u'}}'
            title = u'{var} - {title}'.format(var=data, title=menuitem[0])
            self.insertVariableBtn.appendMenuItem(title, data)

        self.insertVariableBtn.appendMenuItem(_(u'Other variable...'), None)

        snippetButtonsSizer.Add(self.insertVariableBtn, flag=wx.ALL, border=0)

        # Insert block
        self.insertBlockBtn = PopupButton(
            parent,
            bitmap=wx.Bitmap(os.path.join(self._imagesPath, "block-menu.png")))
        self.insertBlockBtn.SetToolTipString(_(u"Insert block"))

        for menuitem in self._blocksMenuItems:
            data = menuitem[1]
            title = menuitem[0]
            self.insertBlockBtn.appendMenuItem(title, data)

        snippetButtonsSizer.Add(self.insertBlockBtn, flag=wx.ALL, border=0)

    def _createSnippetPanel(self, mainSizer):
        self._snippetPanel = wx.Panel(self)

        # Snippet editor
        self.snippetEditor = SnippetEditor(self._snippetPanel)

        # Buttons for snippet
        snippetButtonsSizer = wx.BoxSizer(wx.HORIZONTAL)
        self._createSnippetButtons(snippetButtonsSizer, self._snippetPanel)

        # Errors messages
        self.errorsTextCtrl = wx.TextCtrl(self._snippetPanel,
                                          style=wx.TE_MULTILINE
                                          | wx.TE_READONLY)
        self.errorsTextCtrl.SetMinSize((-1, 100))

        # SnippetSizer
        snippetSizer = wx.FlexGridSizer(cols=1)
        snippetSizer.AddGrowableRow(1)
        snippetSizer.AddGrowableCol(0)

        snippetSizer.Add(snippetButtonsSizer, 1, wx.EXPAND, border=2)
        snippetSizer.Add(self.snippetEditor, 1, wx.EXPAND, border=2)
        snippetSizer.Add(self.errorsTextCtrl, 1, wx.EXPAND, border=2)

        self._snippetPanel.SetSizer(snippetSizer)
        mainSizer.Add(self._snippetPanel, 1, wx.ALL | wx.EXPAND, border=2)

    def _createBottomButtons(self, mainSizer):
        mainSizer.AddStretchSpacer()
        self.closeBtn = wx.Button(self, id=wx.ID_CLOSE, label=_(u'Close'))
        mainSizer.Add(self.closeBtn, flag=wx.ALL | wx.ALIGN_RIGHT, border=2)
        self.SetEscapeId(wx.ID_CLOSE)

    def _createGUI(self):
        # Main Sizer
        mainSizer = wx.FlexGridSizer(cols=2)
        mainSizer.AddGrowableCol(0, 1)
        mainSizer.AddGrowableCol(1, 3)
        mainSizer.AddGrowableRow(0)

        self._createTreePanel(mainSizer)
        self._createSnippetPanel(mainSizer)
        self._createBottomButtons(mainSizer)

        self.SetSizer(mainSizer)
        self.Layout()

    @property
    def currentSnippet(self):
        return self.snippetEditor.GetText()

    def setError(self, text):
        self.errorsTextCtrl.SetValue(text)
Example #4
0
class IconsetPanel(BasePrefPanel):
    def __init__(self, parent):
        super(type(self), self).__init__(parent)

        self._default_group_cover = os.path.join(getImagesDir(),
                                                 u'icons_cover_default.png')

        self.__createGuiElements()

        self._groups.Bind(wx.EVT_TREE_SEL_CHANGED,
                          handler=self.__onGroupSelect)

        self._groups.Bind(wx.EVT_TREE_BEGIN_LABEL_EDIT,
                          self.__onBeginLabelEdit)

        self._groups.Bind(wx.EVT_TREE_END_LABEL_EDIT,
                          self.__onEndLabelEdit)

        self._groups.Bind(wx.EVT_KEY_DOWN, handler=self.__onKeyDown)

        self.__updateGroups()
        self.SetupScrolling()

    def __createGuiElements(self):
        mainSizer = wx.FlexGridSizer(cols=2, rows=1, vgap=0, hgap=0)
        mainSizer.AddGrowableCol(1)
        mainSizer.AddGrowableRow(0)

        #
        # Controls for groups
        groupsSizer = wx.FlexGridSizer(cols=1, rows=0, vgap=0, hgap=0)
        groupsSizer.AddGrowableCol(0)
        groupsSizer.AddGrowableRow(0)

        self._groups = wx.TreeCtrl(
            self,
            style=wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.SUNKEN_BORDER)
        self._groups.SetMinSize((200, 200))

        self._imagelist = SafeImageList(ICON_WIDTH, ICON_HEIGHT)
        self._groups.AssignImageList(self._imagelist)

        imagesDir = getImagesDir()

        # Buttons for groups
        groupButtonsSizer = wx.BoxSizer(wx.HORIZONTAL)

        # Add a group
        self.addGroupBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "add.png"))
        )
        self.addGroupBtn.SetToolTip(_(u"Add new group"))
        self.addGroupBtn.Bind(wx.EVT_BUTTON, handler=self.__onAddGroup)

        # Remove a group
        self.removeGroupBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "remove.png"))
        )
        self.removeGroupBtn.SetToolTip(_(u"Remove group"))
        self.removeGroupBtn.Bind(wx.EVT_BUTTON, handler=self.__onRemoveGroup)

        # Rename a group
        self.renameGroupBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "pencil.png"))
        )
        self.renameGroupBtn.SetToolTip(_(u"Rename group"))
        self.renameGroupBtn.Bind(wx.EVT_BUTTON, handler=self.__onRenameGroup)

        groupButtonsSizer.Add(self.addGroupBtn, flag=wx.ALL, border=0)
        groupButtonsSizer.Add(self.removeGroupBtn, flag=wx.ALL, border=0)
        groupButtonsSizer.Add(self.renameGroupBtn, flag=wx.ALL, border=0)

        groupsSizer.Add(self._groups, 1, wx.RIGHT | wx.EXPAND, border=2)
        groupsSizer.Add(groupButtonsSizer, 1, wx.RIGHT | wx.EXPAND, border=2)

        #
        # Controls for icons in the group
        iconsSizer = wx.FlexGridSizer(cols=1, rows=0, vgap=0, hgap=0)
        iconsSizer.AddGrowableRow(0)
        iconsSizer.AddGrowableCol(0)

        self._iconsList = IconListCtrl(self, True)
        self._iconsList.SetMinSize((200, 150))

        # Buttons for icons in the group
        iconsButtonsSizer = wx.BoxSizer(wx.HORIZONTAL)

        # Add icons
        self.addIconsBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "add.png"))
        )
        self.addIconsBtn.SetToolTip(_(u"Add icons"))
        self.addIconsBtn.Bind(wx.EVT_BUTTON, handler=self.__onAddIcons)

        # Remove icons
        self.removeIconsBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "remove.png"))
        )
        self.removeIconsBtn.SetToolTip(_(u"Remove selected icons"))
        self.removeIconsBtn.Bind(wx.EVT_BUTTON, handler=self.__onRemoveIcons)

        # Set icon as group cover
        self.setCoverBtn = wx.BitmapButton(
            self,
            bitmap=wx.Bitmap(os.path.join(imagesDir, "picture.png"))
        )
        self.setCoverBtn.SetToolTip(_(u"Set icon as group cover"))
        self.setCoverBtn.Bind(wx.EVT_BUTTON, handler=self.__onSetCover)

        iconsButtonsSizer.Add(self.addIconsBtn, flag=wx.ALL, border=0)
        iconsButtonsSizer.Add(self.removeIconsBtn, flag=wx.ALL, border=0)
        iconsButtonsSizer.Add(self.setCoverBtn, flag=wx.ALL, border=0)

        iconsSizer.Add(self._iconsList, 1, wx.LEFT | wx.EXPAND, border=2)
        iconsSizer.Add(iconsButtonsSizer, 1, wx.LEFT | wx.EXPAND, border=2)

        # Main sizer
        mainSizer.Add(groupsSizer, 1, wx.ALL | wx.EXPAND, border=0)
        mainSizer.Add(iconsSizer, 1, wx.ALL | wx.EXPAND, border=0)

        self.SetSizer(mainSizer)
        self.Layout()

    def Save(self):
        pass

    def LoadState(self):
        pass

    def __updateGroups(self):
        self._groups.DeleteAllItems()
        self._imagelist.RemoveAll()

        collection = self.__getIconsCollection()

        # Add the root element
        rootimage = collection.getCover(None)
        imageIndex = -1 if rootimage is None else self._imagelist.Add(wx.Bitmap(rootimage))
        rootItem = self._groups.AddRoot(_(u"Not in groups"),
                                        imageIndex,
                                        data=None)

        # Add child groups
        for group in collection.getGroups():
            image = collection.getCover(group)
            if image is None:
                image = self._default_group_cover

            imageIndex = self._imagelist.Add(wx.Bitmap(image))

            self._groups.AppendItem(rootItem,
                                    group,
                                    imageIndex,
                                    data=group)

        self._groups.Expand(rootItem)
        self._groups.SelectItem(rootItem)
        self.__onGroupSelect(None)

    def __getIconsCollection(self):
        return IconsCollection(getIconsDirList()[-1])

    def __showIcons(self, groupname):
        """
        Show icons from group groupname.
        If groupname is None then icons from root will be showed
        """
        self._iconsList.clear()
        collection = self.__getIconsCollection()
        icons = collection.getIcons(groupname)
        self._iconsList.setIconsList(icons)

    def __onGroupSelect(self, event):
        """
        User select other group
        """
        selItem = self._groups.GetSelection()
        if not selItem.IsOk():
            return

        group = self._groups.GetItemData(selItem)
        self.__showIcons(group)

    def __onAddGroup(self, event):
        collection = self.__getIconsCollection()
        newGroupName = self.__getNewGroupName(collection.getGroups())
        try:
            collection.addGroup(newGroupName)
            self.__updateGroups()
            self.__selectGroupItem(newGroupName)
        except(IOError, SystemError):
            MessageBox(
                _(u"Can't create directory for icons group"),
                _(u"Error"),
                wx.OK | wx.ICON_ERROR)

    def __selectGroupItem(self, groupname):
        """
        Select group in _groups tree. If groupname is None then select
        the root element.
        If groupname not exists then method does nothing.
        """
        rootItem = self._groups.GetRootItem()
        assert rootItem.IsOk()

        if groupname is None:
            self._groups.SelectItem(rootItem)

        nextGroupItem, cookie = self._groups.GetFirstChild(rootItem)

        while nextGroupItem.IsOk():
            if self._groups.GetItemData(nextGroupItem) == groupname:
                self._groups.SelectItem(nextGroupItem)
                break

            nextGroupItem, cookie = self._groups.GetNextChild(rootItem, cookie)

    def __getNewGroupName(self, groups):
        """
        Return name for new group
        """
        newGroupTemplate = _(u"New group{}")
        newGroupName = newGroupTemplate.format(u"")
        if newGroupName in groups:
            # Generate new group name in format "New group(1)",
            # "New group(2)" etc
            index = 0
            while newGroupName in groups:
                index += 1
                newGroupName = newGroupTemplate.format(u"({})".format(index))
        return newGroupName

    def __onEndLabelEdit(self, event):
        if event.IsEditCancelled():
            return

        event.Veto()
        oldGroupName = self._groups.GetItemData(event.GetItem())
        newGroupName = event.GetLabel().strip()

        assert oldGroupName is not None

        collection = self.__getIconsCollection()

        try:
            collection.renameGroup(oldGroupName, newGroupName)
        except(IOError, SystemError):
            MessageBox(
                _(u"Can't rename directory for icons group"),
                _(u"Error"),
                wx.OK | wx.ICON_ERROR)
            return
        except DuplicateGroupError:
            MessageBox(
                _(u'Group with name "{}" exists already').format(newGroupName),
                _(u"Error"),
                wx.OK | wx.ICON_ERROR)
            return
        except ValueError:
            MessageBox(
                _(u'Invalid group name "{}"').format(newGroupName),
                _(u"Error"),
                wx.OK | wx.ICON_ERROR)
            return

        self.__updateGroups()
        self.__selectGroupItem(newGroupName)

    def __onBeginLabelEdit(self, event):
        item = event.GetItem()
        group = self._groups.GetItemData(item)
        if group is None:
            # Root element
            event.Veto()

    def __onRenameGroup(self, event):
        selItem = self._groups.GetSelection()
        rootItem = self._groups.GetRootItem()

        if selItem.IsOk() and selItem != rootItem:
            self._groups.EditLabel(selItem)

    def __onRemoveGroup(self, event):
        selItem = self._groups.GetSelection()
        rootItem = self._groups.GetRootItem()

        if not selItem.IsOk() or selItem == rootItem:
            return

        groupname = self._groups.GetItemData(selItem)
        assert groupname is not None

        if MessageBox(
                _(u'Remove group "{}" and all icons inside it?').format(groupname),
                _(u"Remove group?"),
                wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
            try:
                self.__getIconsCollection().removeGroup(groupname)
            except(IOError, SystemError):
                MessageBox(
                    _(u"Can't remove group directory"),
                    _(u"Error"),
                    wx.OK | wx.ICON_ERROR)
                return
            self.__updateGroups()

    def __onAddIcons(self, event):
        wildcard = u"{images}(*.png; *.jpg; *.jpeg; *.gif; *.bmp)|*.png; *.jpg; *.jpeg; *.gif; *.bmp|*.png|*.png|*.jpg; *.jpeg|*.jpg;*.jpeg|*.gif|*.gif|*.bmp|*.bmp|{all}(*.*)|*.*".format(
            images=_(u"All image files"),
            all=_(u"All files"))
        style = wx.FD_OPEN | wx.FD_MULTIPLE | wx.FD_FILE_MUST_EXIST

        with TestedFileDialog(
                self,
                _(u"Select images"),
                wildcard=wildcard,
                style=style) as dlg:
            if dlg.ShowModal() == wx.ID_OK:
                item = self._groups.GetSelection()
                group = self._groups.GetItemData(item)

                collection = self.__getIconsCollection()
                collection.addIcons(group, dlg.GetPaths())
                self.__updateGroups()
                self.__selectGroupItem(group)

    def __onRemoveIcons(self, event):
        icons = self._iconsList.getSelection()
        if not icons:
            MessageBox(
                _(u"You have not selected any icons"),
                _(u"Select icons"),
                wx.OK | wx.ICON_INFORMATION)
            return

        if MessageBox(
                _(u"Remove selected icons?"),
                _(u"Remove icons"),
                wx.YES_NO | wx.ICON_QUESTION) == wx.YES:
            for fname in icons:
                try:
                    os.remove(fname)
                except(IOError, SystemError):
                    pass

            item = self._groups.GetSelection()
            group = self._groups.GetItemData(item)
            self.__updateGroups()
            self.__selectGroupItem(group)

    def __onSetCover(self, event):
        icons = self._iconsList.getSelection()
        if not icons:
            MessageBox(
                _(u"You have not selected any icons"),
                _(u"Select icons"),
                wx.OK | wx.ICON_ERROR)
            return

        item = self._groups.GetSelection()
        group = self._groups.GetItemData(item)

        collection = self.__getIconsCollection()
        collection.setCover(group, icons[0])

        self.__updateGroups()
        self.__selectGroupItem(group)

    def __onKeyDown(self, event):
        if (event.GetKeyCode() == wx.WXK_F2 and
                not event.AltDown() and
                not event.CmdDown() and
                not event.ControlDown() and
                not event.ShiftDown()):
            self.__onRenameGroup(None)
Example #5
0
class NotesTree(wx.Panel):
    def __init__(self, parent, application):
        super().__init__(parent, style=wx.TAB_TRAVERSAL)
        self._application = application
        # Переключатель, устанавливается в True,
        # если "внезапно" изменяется текущая страница
        self.__externalPageSelect = False

        self.toolbar = wx.ToolBar(parent=self,
                                  style=wx.TB_HORIZONTAL | wx.TB_FLAT
                                  | wx.TB_DOCKABLE)

        treeStyle = (wx.TR_HAS_BUTTONS | wx.TR_EDIT_LABELS | wx.SUNKEN_BORDER)

        self.treeCtrl = wx.TreeCtrl(self, style=treeStyle)

        self.__set_properties()
        self.__do_layout()

        self.defaultIcon = os.path.join(outwiker.core.system.getImagesDir(),
                                        "page.png")
        self.iconHeight = ICON_HEIGHT

        self.defaultBitmap = wx.Bitmap(self.defaultIcon)
        assert self.defaultBitmap.IsOk()

        self.defaultBitmap.SetHeight(self.iconHeight)

        # Key - path to icon, value - icon ID in self.imagelist
        self._iconsCache = {}

        self.dragItem = None

        # Картинки для дерева
        self.imagelist = SafeImageList(ICON_WIDTH, self.iconHeight)
        self.treeCtrl.AssignImageList(self.imagelist)

        # Кеш для страниц, чтобы было проще искать элемент дерева по странице
        # Словарь. Ключ - страница, значение - элемент дерева wx.TreeItemId
        self._pageCache = {}

        self.popupMenu = None

        # Секция настроек куда сохраняем развернутость страницы
        self.pageOptionsSection = u"Tree"

        # Имя опции для сохранения развернутости страницы
        self.pageOptionExpand = "Expand"

        self.__BindApplicationEvents()
        self.__BindGuiEvents()

    def getTreeItem(self, page):
        """
        Получить элемент дерева по странице.
        Если для страницы не создан элемент дерева, возвращается None
        """
        if page in self._pageCache:
            return self._pageCache[page]

    def __BindApplicationEvents(self):
        """
        Подписка на события контроллера
        """
        self._application.onWikiOpen += self.__onWikiOpen
        self._application.onTreeUpdate += self.__onTreeUpdate
        self._application.onPageCreate += self.__onPageCreate
        self._application.onPageOrderChange += self.__onPageOrderChange
        self._application.onPageSelect += self.__onPageSelect
        self._application.onPageRemove += self.__onPageRemove
        self._application.onPageUpdate += self.__onPageUpdate

        self._application.onStartTreeUpdate += self.__onStartTreeUpdate
        self._application.onEndTreeUpdate += self.__onEndTreeUpdate

    def __UnBindApplicationEvents(self):
        """
        Отписка от событий контроллера
        """
        self._application.onWikiOpen -= self.__onWikiOpen
        self._application.onTreeUpdate -= self.__onTreeUpdate
        self._application.onPageCreate -= self.__onPageCreate
        self._application.onPageOrderChange -= self.__onPageOrderChange
        self._application.onPageSelect -= self.__onPageSelect
        self._application.onPageRemove -= self.__onPageRemove
        self._application.onPageUpdate -= self.__onPageUpdate

        self._application.onStartTreeUpdate -= self.__onStartTreeUpdate
        self._application.onEndTreeUpdate -= self.__onEndTreeUpdate

    def __onWikiOpen(self, root):
        self.__treeUpdate(root)

    def __onPageUpdate(self, sender, **kwargs):
        change = kwargs['change']
        if change == PAGE_UPDATE_ICON:
            self.__updateIcon(sender)

    def __loadIcon(self, page):
        """
        Добавляет иконку страницы в ImageList и возвращает ее идентификатор.
        Если иконки нет, то возвращает идентификатор иконки по умолчанию
        """
        imageId = self.defaultImageId
        icon = page.icon

        if not icon:
            return imageId

        icon = os.path.abspath(icon)

        if icon in self._iconsCache:
            return self._iconsCache[icon]

        image = wx.Bitmap(icon)
        if image.IsOk():
            imageId = self.imagelist.Add(image)

        page_path = os.path.abspath(page.path)
        if not icon.startswith(page_path):
            self._iconsCache[icon] = imageId

        return imageId

    def __updateIcon(self, page):
        if page not in self._pageCache:
            # Если нет этой страницы в дереве, то не важно,
            # изменилась иконка или нет
            return

        icon_id = self.__loadIcon(page)
        self.treeCtrl.SetItemImage(self._pageCache[page], icon_id)

    def __BindGuiEvents(self):
        """
        Подписка на события интерфейса
        """
        # События, связанные с деревом
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.__onSelChanged)
        self.Bind(wx.EVT_TREE_ITEM_MIDDLE_CLICK, self.__onMiddleClick)

        # Перетаскивание элементов
        self.treeCtrl.Bind(wx.EVT_TREE_BEGIN_DRAG, self.__onBeginDrag)
        self.treeCtrl.Bind(wx.EVT_TREE_END_DRAG, self.__onEndDrag)

        # Переименование элемента
        self.treeCtrl.Bind(wx.EVT_TREE_END_LABEL_EDIT, self.__onEndLabelEdit)

        # Показ всплывающего меню
        self.treeCtrl.Bind(wx.EVT_TREE_ITEM_MENU, self.__onPopupMenu)

        # Сворачивание/разворачивание элементов
        self.treeCtrl.Bind(wx.EVT_TREE_ITEM_COLLAPSED,
                           self.__onTreeStateChanged)
        self.treeCtrl.Bind(wx.EVT_TREE_ITEM_EXPANDED,
                           self.__onTreeStateChanged)
        self.treeCtrl.Bind(wx.EVT_TREE_ITEM_ACTIVATED,
                           self.__onTreeItemActivated)

        self.Bind(wx.EVT_CLOSE, self.__onClose)

    def __onMiddleClick(self, event):
        item = event.GetItem()
        if not item.IsOk():
            return

        page = self.treeCtrl.GetItemData(item)
        self._application.mainWindow.tabsController.openInTab(page, True)

    def __onClose(self, event):
        self.__UnBindApplicationEvents()
        self.treeCtrl.DeleteAllItems()
        self.imagelist.RemoveAll()
        self._iconsCache = {}
        self._removeButtons()
        self.toolbar.ClearTools()
        self.Destroy()

    def __onPageCreate(self, newpage):
        """
        Обработка создания страницы
        """
        if newpage.parent in self._pageCache:
            self.__insertChild(newpage)

            assert newpage in self._pageCache
            item = self._pageCache[newpage]
            assert item.IsOk()

            self.expand(newpage)

    def __onPageRemove(self, page):
        self.__removePageItem(page)

    def __onTreeItemActivated(self, event):
        item = event.GetItem()
        if not item.IsOk():
            return

        page = self.treeCtrl.GetItemData(item)
        outwiker.gui.pagedialog.editPage(self, page)

    def __onTreeStateChanged(self, event):
        item = event.GetItem()
        assert item.IsOk()
        page = self.treeCtrl.GetItemData(item)
        self.__saveItemState(item)

        for child in page.children:
            self.__appendChildren(child)

    def __saveItemState(self, itemid):
        assert itemid.IsOk()

        page = self.treeCtrl.GetItemData(itemid)
        if page.readonly:
            return

        expanded = self.treeCtrl.IsExpanded(itemid)

        page_registry = page.root.registry.get_page_registry(page)
        page_registry.set(self.pageOptionExpand, expanded)

    def __getItemExpandState(self, page):
        """
        Проверить, раскрыт ли элемент в дереве для страницы page
        """
        if page is None:
            return True

        if page.parent is None:
            return True

        return self.treeCtrl.IsExpanded(self._pageCache[page])

    def __getPageExpandState(self, page):
        """
        Проверить состояние "раскрытости" страницы
        """
        if page is None:
            return True

        if page.parent is None:
            return True

        page_registry = page.root.registry.get_page_registry(page)
        expanded = page_registry.getbool(self.pageOptionExpand, default=False)

        return expanded

    def __onPopupMenu(self, event):
        self.popupPage = None
        popupItem = event.GetItem()
        if not popupItem.IsOk():
            return

        popupPage = self.treeCtrl.GetItemData(popupItem)
        self.popupMenu = PagePopupMenu(self, popupPage, self._application)
        self.PopupMenu(self.popupMenu.menu)

    def beginRename(self, page=None):
        """
        Начать переименование страницы в дереве. Если page is None,
        то начать переименование текущей страницы
        """
        pageToRename = page if page is not None else self._application.selectedPage

        if pageToRename is None or pageToRename.parent is None:
            outwiker.core.commands.MessageBox(
                _(u"You can't rename the root element"), _(u"Error"),
                wx.ICON_ERROR | wx.OK)
            return

        selectedItem = self._pageCache[pageToRename]
        if not selectedItem.IsOk():
            return

        self.treeCtrl.EditLabel(selectedItem)

    def __onEndLabelEdit(self, event):
        if event.IsEditCancelled():
            return

        # Новый заголовок
        label = event.GetLabel().strip()

        item = event.GetItem()
        page = self.treeCtrl.GetItemData(item)
        # Не доверяем переименовывать элементы системе
        event.Veto()

        outwiker.core.commands.renamePage(page, label)

    def __onStartTreeUpdate(self, root):
        self.__unbindUpdateEvents()

    def __unbindUpdateEvents(self):
        self._application.onTreeUpdate -= self.__onTreeUpdate
        self._application.onPageCreate -= self.__onPageCreate
        self._application.onPageSelect -= self.__onPageSelect
        self._application.onPageOrderChange -= self.__onPageOrderChange
        self.Unbind(wx.EVT_TREE_SEL_CHANGED, handler=self.__onSelChanged)

    def __onEndTreeUpdate(self, root):
        self.__bindUpdateEvents()
        self.__treeUpdate(self._application.wikiroot)

    def __bindUpdateEvents(self):
        self._application.onTreeUpdate += self.__onTreeUpdate
        self._application.onPageCreate += self.__onPageCreate
        self._application.onPageSelect += self.__onPageSelect
        self._application.onPageOrderChange += self.__onPageOrderChange
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.__onSelChanged)

    def __onBeginDrag(self, event):
        event.Allow()
        self.dragItem = event.GetItem()
        self.treeCtrl.SetFocus()

    def __onEndDrag(self, event):
        if self.dragItem is not None:
            # Элемент, на который перетащили другой элемент(self.dragItem)
            endDragItem = event.GetItem()

            # Перетаскиваемая станица
            draggedPage = self.treeCtrl.GetItemData(self.dragItem)

            # Будущий родитель для страницы
            if endDragItem.IsOk():
                newParent = self.treeCtrl.GetItemData(endDragItem)

                # Moving page to itself is ignored
                if newParent != draggedPage:
                    outwiker.core.commands.movePage(draggedPage, newParent)
                    self.expand(newParent)

        self.dragItem = None

    def __onTreeUpdate(self, sender):
        self.__treeUpdate(sender.root)

    def __onPageSelect(self, page):
        """
        Изменение выбранной страницы
        """
        # Пометим, что изменение страницы произошло снаружи,
        # а не из-за клика по дереву
        self.__externalPageSelect = True

        try:
            currpage = self.selectedPage
            if currpage != page:
                self.selectedPage = page
        finally:
            self.__externalPageSelect = False

    def __onSelChanged(self, event):
        ctrlstate = wx.GetKeyState(wx.WXK_CONTROL)
        shiftstate = wx.GetKeyState(wx.WXK_SHIFT)

        if (ctrlstate or shiftstate) and not self.__externalPageSelect:
            self._application.mainWindow.tabsController.openInTab(
                self.selectedPage, True)
        else:
            self._application.selectedPage = self.selectedPage

    def __onPageOrderChange(self, sender):
        """
        Изменение порядка страниц
        """
        self.__updatePage(sender)

    @property
    def selectedPage(self):
        page = None

        item = self.treeCtrl.GetSelection()
        if item.IsOk():
            page = self.treeCtrl.GetItemData(item)

            # Проверка того, что выбрали не корневой элемент
            if page.parent is None:
                page = None

        return page

    @selectedPage.setter
    def selectedPage(self, newSelPage):
        if newSelPage is None:
            item = self.treeCtrl.GetRootItem()
        else:
            self.__expandToPage(newSelPage)
            item = self.getTreeItem(newSelPage)

        if item is not None:
            self.treeCtrl.SelectItem(item)

    def __expandToPage(self, page):
        """
        Развернуть ветви до того уровня, чтобы появился элемент для page
        """
        # Список родительских страниц, которые нужно добавить в дерево
        pages = []
        currentPage = page.parent
        while currentPage is not None:
            pages.append(currentPage)
            currentPage = currentPage.parent

        pages.reverse()
        for page in pages:
            self.expand(page)

    def __set_properties(self):
        self.SetSize((256, 260))

    def __do_layout(self):
        mainSizer = wx.FlexGridSizer(cols=1)
        mainSizer.AddGrowableRow(1)
        mainSizer.AddGrowableCol(0)
        mainSizer.Add(self.toolbar, 1, wx.EXPAND, 0)
        mainSizer.Add(self.treeCtrl, 1, wx.EXPAND, 0)
        self.SetSizer(mainSizer)
        self.Layout()

    def expand(self, page):
        item = self.getTreeItem(page)
        if item is not None:
            self.treeCtrl.Expand(item)

    def __treeUpdate(self, rootPage):
        """
        Обновить дерево
        """
        self.treeCtrl.DeleteAllItems()
        self.imagelist.RemoveAll()
        self._iconsCache = {}
        self.defaultImageId = self.imagelist.Add(self.defaultBitmap)

        # Ключ - страница, значение - экземпляр класса TreeItemId
        self._pageCache = {}

        if rootPage is not None:
            rootname = os.path.basename(rootPage.path)
            rootItem = self.treeCtrl.AddRoot(rootname,
                                             data=rootPage,
                                             image=self.defaultImageId)

            self._pageCache[rootPage] = rootItem
            self.__mountItem(rootItem, rootPage)
            self.__appendChildren(rootPage)

            self.selectedPage = rootPage.selectedPage
            self.expand(rootPage)

    def __appendChildren(self, parentPage):
        """
        Добавить детей в дерево
        parentPage - родительская страница, куда добавляем дочерние страницы
        """
        grandParentExpanded = self.__getItemExpandState(parentPage.parent)

        if grandParentExpanded:
            for child in parentPage.children:
                if child not in self._pageCache:
                    self.__insertChild(child)

        if self.__getPageExpandState(parentPage):
            self.expand(parentPage)

    def __mountItem(self, treeitem, page):
        """
        Оформить элемент дерева в зависимости от настроек страницы
        (например, пометить только для чтения)
        """
        if page.readonly:
            font = wx.SystemSettings.GetFont(wx.SYS_DEFAULT_GUI_FONT)
            font.SetStyle(wx.FONTSTYLE_ITALIC)
            self.treeCtrl.SetItemFont(treeitem, font)

    def __insertChild(self, child):
        """
        Вставить одну дочерниюю страницу(child) в ветвь
        """
        parentItem = self.getTreeItem(child.parent)
        assert parentItem is not None

        item = self.treeCtrl.InsertItem(parentItem,
                                        child.order,
                                        child.display_title,
                                        data=child)

        self.treeCtrl.SetItemImage(item, self.__loadIcon(child))

        self._pageCache[child] = item
        self.__mountItem(item, child)
        self.__appendChildren(child)

        return item

    def __removePageItem(self, page):
        """
        Удалить элемент, соответствующий странице и все его дочерние страницы
        """
        for child in page.children:
            self.__removePageItem(child)

        item = self.getTreeItem(page)
        if item is not None:
            del self._pageCache[page]
            self.treeCtrl.Delete(item)

    def __updatePage(self, page):
        """
        Обновить страницу(удалить из списка и добавить снова)
        """
        # Отпишемся от обновлений страниц, чтобы не изменять выбранную страницу
        self.__unbindUpdateEvents()
        self.treeCtrl.Freeze()

        try:
            self.__removePageItem(page)

            item = self.__insertChild(page)

            if page.root.selectedPage == page:
                # Если обновляем выбранную страницу
                self.treeCtrl.SelectItem(item)

            self.__scrollToCurrentPage()
        finally:
            self.treeCtrl.Thaw()
            self.__bindUpdateEvents()

    def __scrollToCurrentPage(self):
        """
        Если текущая страница вылезла за пределы видимости, то прокрутить к ней
        """
        selectedPage = self._application.selectedPage
        if selectedPage is None:
            return

        item = self.getTreeItem(selectedPage)
        if not self.treeCtrl.IsVisible(item):
            self.treeCtrl.ScrollTo(item)

    def addButtons(self):
        """
        Add the buttons to notes tree panel.
        """
        imagesDir = outwiker.core.system.getImagesDir()
        actionController = self._application.actionController

        actionController.appendToolbarButton(
            GoToParentAction.stringId, self.toolbar,
            os.path.join(imagesDir, "go_to_parent.png"), False)

        self.toolbar.AddSeparator()

        actionController.appendToolbarButton(
            MovePageDownAction.stringId, self.toolbar,
            os.path.join(imagesDir, "move_down.png"), False)

        actionController.appendToolbarButton(
            MovePageUpAction.stringId, self.toolbar,
            os.path.join(imagesDir, "move_up.png"), False)

        self.toolbar.AddSeparator()

        actionController.appendToolbarButton(
            AddSiblingPageAction.stringId, self.toolbar,
            os.path.join(imagesDir, "node-insert-next.png"), False)

        actionController.appendToolbarButton(
            AddChildPageAction.stringId, self.toolbar,
            os.path.join(imagesDir, "node-insert-child.png"), False)

        actionController.appendToolbarButton(
            RemovePageAction.stringId, self.toolbar,
            os.path.join(imagesDir, "node-delete.png"), False)

        self.toolbar.AddSeparator()

        actionController.appendToolbarButton(
            EditPagePropertiesAction.stringId, self.toolbar,
            os.path.join(imagesDir, "edit.png"), False)

        self.toolbar.Realize()
        self.Layout()

    def _removeButtons(self):
        actionController = self._application.actionController

        actions = [
            GoToParentAction,
            MovePageDownAction,
            MovePageUpAction,
            AddSiblingPageAction,
            AddChildPageAction,
            RemovePageAction,
            EditPagePropertiesAction,
        ]
        for action in actions:
            actionController.removeToolbarButton(action.stringId)