#! /usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
##                                                                           ##
##  Copyright 2011, Neil Wallace <rowinggolfer@googlemail.com>               ##
##                                                                           ##
##  This program is free software: you can redistribute it and/or modify     ##
##  it under the terms of the GNU General Public License as published by     ##
##  the Free Software Foundation, either version 3 of the License, or        ##
##  (at your option) any later version.                                      ##
##                                                                           ##
##  This program is distributed in the hope that it will be useful,          ##
##  but WITHOUT ANY WARRANTY; without even the implied warranty of           ##
##  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            ##
##  GNU General Public License for more details.                             ##
##                                                                           ##
##  You should have received a copy of the GNU General Public License        ##
##  along with this program.  If not, see <http://www.gnu.org/licenses/>.    ##
##                                                                           ##
###############################################################################
'''
A simple application with a shrinkable menu bar.
(similar functionality to firefox)
At any point either the tiny menu or menubar should be visible.
Therefore if the toolbar is hidden, the menu will re-appear.
to experience this... either
    click View -> Tiny Menu
    or use the keyboard shortcut "ctrl M"
'''
import logging
from PyQt4 import QtGui, QtCore
class _DockableToolButton(QtGui.QToolButton):
    '''
    A toolbutton which acts nicely with a toolbar.
    '''
    def __init__(self, parent=None):
        QtGui.QToolButton.__init__(self, parent)
        try:
            icon = self.parent().windowIcon()
        except AttributeError:
            logging.debug("using fallback icon for tiny menu")
            icon = QtGui.QIcon.fromTheme("go-down")
        self.setIcon(icon)
        self.setPopupMode(QtGui.QToolButton.InstantPopup)
        self.setText(_("Menu"))
        self.setToolButtonStyle(QtCore.Qt.ToolButtonFollowStyle)
class _MenuToolBar(QtGui.QToolBar):
    def __init__(self, parent=None):
        QtGui.QToolBar.__init__(self, parent)
        self.setToolButtonStyle(QtCore.Qt.ToolButtonTextBesideIcon)
        self.setObjectName("_MenuToolbar") #for QSettings
        # this should happen by default IMO.
        self.toggleViewAction().setText(_("TinyMenu"))
        self.toggleViewAction().setShortcut('ctrl+M')
        #self.toggleViewAction().toggled.connect(self.clear_mini_menu)
        self._menu_button = None
        self.hide()
    def add_mini_menu(self, menu_button):
        if not self._menu_button is None:
            self.clear_mini_menu(True)
        self._menu_button = menu_button
        if self.actions():
            self.insertWidget(self.actions()[0], menu_button)
        else:
            self.addWidget(menu_button)
        self.show()
    def clear_mini_menu(self, clear=False):
        '''
        by default this does nothing,
        if called by a positive action (only possible if the menu is already
        initiated) then the menu is cleared and hidden
        '''
        if clear:
            self._menu_button.hide()
            self._menu_button.setParent(None)
            self._menu_button.deleteLater()
            self._menu_button = None
            self.hide()
[docs]class DockableMenuBar(QtGui.QMenuBar):
    '''
    inherits from QMenuBar, adding the functionality to become a
    standalone widget (thus saving screen estate)
    '''
[docs]    def __init__(self, parent):
        QtGui.QMenuBar.__init__(self, parent)
        self._menu_button = None
        #: the toolbar for the mini menu
        self.menu_toolbar = _MenuToolBar(parent)
        self.parent().addToolBar(self.menu_toolbar)
        self.menu_toolbar.toggleViewAction().triggered.connect(
            self.toggle_visability)
        #:
        self.menu_view = QtGui.QMenu(_("&View"), self)
        self.addMenu(self.menu_view)
        self.menu_view.addAction(self.menu_toolbar.toggleViewAction())
        self.menu_view.addSeparator()
        #:
        self.toolbar_menu = QtGui.QMenu(_("&Toolbars"), self)
        self.menu_view.addMenu(self.toolbar_menu)
        #:
        self.toolbar_options_menu = QtGui.QMenu(_("&Options"), self)
        self._populate_toolbar_options()
        self.toolbar_menu.addMenu(self.toolbar_options_menu)
        self.update_toolbars()
    def _populate_toolbar_options(self):
        action_toolbar_opt4 = QtGui.QAction(_("Default View"), self)
        action_toolbar_opt4.setData(QtCore.Qt.ToolButtonFollowStyle)
        action_toolbar_opt0 = QtGui.QAction(_("Icon Only"), self)
        action_toolbar_opt0.setData(QtCore.Qt.ToolButtonIconOnly)
        action_toolbar_opt1 = QtGui.QAction(_("Text Only"), self)
        action_toolbar_opt1.setData(QtCore.Qt.ToolButtonTextOnly)
        action_toolbar_opt2 = QtGui.QAction(_("Text Beside Icon"), self)
        action_toolbar_opt2.setData(QtCore.Qt.ToolButtonTextBesideIcon)
        action_toolbar_opt3 = QtGui.QAction(_("Text Under Icon"), self)
        action_toolbar_opt3.setData(QtCore.Qt.ToolButtonTextUnderIcon)
        self.toolbar_options_menu.addAction(action_toolbar_opt4)
        self.toolbar_options_menu.addAction(action_toolbar_opt0)
        self.toolbar_options_menu.addAction(action_toolbar_opt1)
        self.toolbar_options_menu.addAction(action_toolbar_opt2)
        self.toolbar_options_menu.addAction(action_toolbar_opt3)
        for action in (action_toolbar_opt4, action_toolbar_opt3,
        action_toolbar_opt2, action_toolbar_opt1,
        action_toolbar_opt0):
            action.triggered.connect(self.toolbarButtonType)
    @property
    @property
[docs]    def addViewOption(self, action):
        '''
        add an action to the 'view' category of the menubar
        '''
        self.menu_view.addAction(action)
        self.refresh_mini_menu()
[docs]    def update_toolbars(self):
        '''
        updates the view menu for all the parent application's toolbars
        '''
        for toolbar in self.parent().toolbar_list:
            self.toolbar_menu.addAction(toolbar.toggleViewAction())
        self.refresh_mini_menu()
[docs]    def toolbarButtonType(self):
        '''
        change the appearance of the toolbars
        '''
        styleVariant = self.sender().data()
        style, result = styleVariant.toInt()
        for toolbar in self.known_toolbars():
            toolbar.setToolButtonStyle(style)
            for widg in toolbar.children():
                if type(widg) == QtGui.QToolButton:
                    widg.setToolButtonStyle(style)
[docs]    def addMenu(self, *args):
        try:
            retval = self.insertMenu(self.actions()[-1], *args)
        except IndexError:
            retval = QtGui.QMenuBar.addMenu(self, *args)
        self.refresh_mini_menu()
        return retval
[docs]    def addAction(self, *args):
        try:
            retval = self.insertAction(self.actions()[-1], *args)
        except IndexError:
            retval = QtGui.QMenuBar.addAction(self, *args)
        self.refresh_mini_menu()
        return retval
[docs]    def toggle_visability(self, set_visible):
        self.setVisible(not set_visible)
        self.menu_toolbar.setVisible(set_visible)
        if set_visible:
            self.menu_toolbar.add_mini_menu(self.menu_button)
        else:
            self.menu_toolbar.clear_mini_menu()
[docs]    def setNotVisible(self, menu_bar_visible):
        '''
        make sure that we don't end up with neither menu visible!
        '''
        if not menu_bar_visible:
            self.setVisible(True)
            self.toggleViewAction.setChecked(False)
def _test():
    class _TestMainWindow(QtGui.QMainWindow):
        def __init__(self, parent=None):
            QtGui.QMainWindow.__init__(self, parent)
            self.setWindowIcon(QtGui.QIcon.fromTheme("application-exit"))
            menu_bar = DockableMenuBar(self)
            self.setMenuBar(menu_bar)
            ## initiate instances of our classes
            self.toolbar1 = QtGui.QToolBar(self)
            self.toolbar1.toggleViewAction().setText("%s %s"% (_("&ToolBar"), 1))
            self.toolbar2 = QtGui.QToolBar(self)
            self.toolbar2.toggleViewAction().setText("%s %s"% (_("&ToolBar"), 2))
            self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar1)
            self.addToolBar(QtCore.Qt.TopToolBarArea, self.toolbar2)
            menu_bar.update_toolbars()
            ## some arbitrary stuff to make the app more realistic
            file_action = QtGui.QAction("&File", self)
            self.menuBar().addAction(file_action)
            edit_action = QtGui.QAction("&Edit", self)
            self.menuBar().addAction(edit_action)
            help_icon = QtGui.QIcon.fromTheme("help")
            self.action_help = QtGui.QAction(help_icon, _("Help"), self)
            menu_help = QtGui.QMenu(_("&Help"), self)
            menu_help.addAction(self.action_help)
            self.menuBar().addMenu(menu_help)
            ## and a couple of extras for the toolbar
            icon = QtGui.QIcon.fromTheme("system-file-manager")
            random_action = QtGui.QAction(icon, "function", self)
            self.toolbar1.addAction(random_action)
            ## a typical web address widget
            address_widget = QtGui.QWidget()
            layout = QtGui.QHBoxLayout(address_widget)
            layout.setMargin(0)
            line_edit = QtGui.QLineEdit("http://google.com")
            go_but = QtGui.QPushButton("Go!")
            go_but.setFixedWidth(60)
            layout.addWidget(line_edit)
            layout.addWidget(go_but)
            self.toolbar1.addWidget(address_widget)
            ## and give the second toolbar some content
            self.toolbar2.addAction(self.action_help)
            ## set a central widget
            te = QtGui.QTextEdit()
            self.setCentralWidget(te)
            te.setText(__doc__)
        def sizeHint(self):
            return QtCore.QSize(400,400)
    app = QtGui.QApplication([])
    mw = _TestMainWindow()
    mw.show()
    app.exec_()
if __name__ == "__main__":
    logging.basicConfig(level=logging.DEBUG)
    import gettext
    gettext.install("")
    _test()