diff options
Diffstat (limited to 'mvc/widgets/gtk/window.py')
-rw-r--r-- | mvc/widgets/gtk/window.py | 708 |
1 files changed, 0 insertions, 708 deletions
diff --git a/mvc/widgets/gtk/window.py b/mvc/widgets/gtk/window.py deleted file mode 100644 index 3859a1a..0000000 --- a/mvc/widgets/gtk/window.py +++ /dev/null @@ -1,708 +0,0 @@ -# @Base: Miro - an RSS based video player application -# Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011 -# Jesus Eduardo (Heckyel) | 2017 -# -# 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 2 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, write to the Free Software -# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -# -# In addition, as a special exception, the copyright holders give -# permission to link the code of portions of this program with the OpenSSL -# library. -# -# You must obey the GNU General Public License in all respects for all of -# the code used other than OpenSSL. If you modify file(s) with this -# exception, you may extend this exception to your version of the file(s), -# but you are not obligated to do so. If you do not wish to do so, delete -# this exception statement from your version. If you delete this exception -# statement from all source files in the program, then also delete it here. - -""".window -- GTK Window widget.""" - -import gobject -import gtk -import os - -from mvc import resources -from mvc import signals - -import keymap -import layout -import widgets -import wrappermap - -# keeps the objects alive until destroy() is called -alive_windows = set() -running_dialogs = set() - -class WrappedWindow(gtk.Window): - def do_map(self): - gtk.Window.do_map(self) - wrappermap.wrapper(self).emit('show') - - def do_unmap(self): - gtk.Window.do_unmap(self) - wrappermap.wrapper(self).emit('hide') - def do_focus_in_event(self, event): - gtk.Window.do_focus_in_event(self, event) - wrappermap.wrapper(self).emit('active-change') - def do_focus_out_event(self, event): - gtk.Window.do_focus_out_event(self, event) - wrappermap.wrapper(self).emit('active-change') - - def do_key_press_event(self, event): - if self.activate_key(event): # event activated a menu item - return - - if self.propagate_key_event(event): # event handled by widget - return - - ret = keymap.translate_gtk_event(event) - if ret is not None: - key, modifiers = ret - rv = wrappermap.wrapper(self).emit('key-press', key, modifiers) - if not rv: - gtk.Window.do_key_press_event(self, event) - - def _get_focused_wrapper(self): - """Get the wrapper of the widget with keyboard focus""" - focused = self.get_focus() - # some of our widgets created children for their use - # (GtkSearchTextEntry). If we don't find a wrapper for - # focused, try it's parents - while focused is not None: - try: - wrapper = wrappermap.wrapper(focused) - except KeyError: - focused = focused.get_parent() - else: - return wrapper - return None - - def change_focus_using_wrapper(self, direction): - my_wrapper = wrappermap.wrapper(self) - focused_wrapper = self._get_focused_wrapper() - if direction == gtk.DIR_TAB_FORWARD: - to_focus = my_wrapper.get_next_tab_focus(focused_wrapper, True) - elif direction == gtk.DIR_TAB_BACKWARD: - to_focus = my_wrapper.get_next_tab_focus(focused_wrapper, False) - else: - return False - if to_focus is not None: - to_focus.focus() - return True - return False - - def do_focus(self, direction): - if not self.change_focus_using_wrapper(direction): - gtk.Window.do_focus(self, direction) - -gobject.type_register(WrappedWindow) - -class WindowBase(signals.SignalEmitter): - def __init__(self): - signals.SignalEmitter.__init__(self) - self.create_signal('use-custom-style-changed') - self.create_signal('key-press') - self.create_signal('show') - self.create_signal('hide') - - def set_window(self, window): - self._window = window - window.connect('style-set', self.on_style_set) - wrappermap.add(window, self) - self.calc_use_custom_style() - - def on_style_set(self, widget, old_style): - old_use_custom_style = self.use_custom_style - self.calc_use_custom_style() - if old_use_custom_style != self.use_custom_style: - self.emit('use-custom-style-changed') - - def calc_use_custom_style(self): - if self._window is not None: - base = self._window.style.base[gtk.STATE_NORMAL] - # Decide if we should use a custom style. Right now the - # formula is the base color is a very light shade of - # gray/white (lighter than #f0f0f0). - self.use_custom_style = ((base.red == base.green == base.blue) and - base.red >= 61680) - - -class Window(WindowBase): - """The main Libre window. """ - - def __init__(self, title, rect=None): - """Create the Libre Main Window. Title is the name to give the - window, rect specifies the position it should have on screen. - """ - WindowBase.__init__(self) - self.set_window(self._make_gtk_window()) - self._window.set_title(title) - self.setup_icon() - if rect: - self._window.set_default_size(rect.width, rect.height) - self._window.set_default_size(rect.width, rect.height) - self._window.set_gravity(gtk.gdk.GRAVITY_CENTER) - self._window.move(rect.x, rect.y) - - self.create_signal('active-change') - self.create_signal('will-close') - self.create_signal('did-move') - self.create_signal('file-drag-motion') - self.create_signal('file-drag-received') - self.create_signal('file-drag-leave') - self.create_signal('on-shown') - self.drag_signals = [] - alive_windows.add(self) - - self._window.connect('delete-event', self.on_delete_window) - self._window.connect('map-event', lambda w, a: self.emit('on-shown')) - # XXX: Define MVCWindow/MiroWindow style not hard code this - self._window.set_resizable(False) - - def setup_icon(self): - icon_pixbuf = gtk.gdk.pixbuf_new_from_file( - resources.image_path("mvc-logo.png")) - self._window.set_icon(icon_pixbuf) - - - def accept_file_drag(self, val): - if not val: - self._window.drag_dest_set(0, [], 0) - for handle in self.drag_signals: - self.disconnect(handle) - self.drag_signals = [] - else: - self._window.drag_dest_set( - gtk.DEST_DEFAULT_MOTION | gtk.DEST_DEFAULT_DROP, - [('text/uri-list', 0, 0)], - gtk.gdk.ACTION_COPY) - for signal, callback in ( - ('drag-motion', self.on_drag_motion), - ('drag-data-received', self.on_drag_data_received), - ('drag-leave', self.on_drag_leave)): - self.drag_signals.append( - self._window.connect(signal, callback)) - - def on_drag_motion(self, widget, context, x, y, time): - self.emit('file-drag-motion') - - def on_drag_data_received(self, widget, context, x, y, selection_data, - info, time): - self.emit('file-drag-received', selection_data.get_uris()) - - def on_drag_leave(self, widget, context, time): - self.emit('file-drag-leave') - - def on_delete_window(self, widget, event): - # when the user clicks on the X in the corner of the window we - # want that to close the window, but also trigger our - # will-close signal and all that machinery unless the window - # is currently hidden--then we don't do anything. - if not self._window.window.is_visible(): - return - self.close() - return True - - def _make_gtk_window(self): - return WrappedWindow() - - def set_title(self, title): - self._window.set_title(title) - - def get_title(self): - self._window.get_title() - - def center(self): - self._window.set_position(gtk.WIN_POS_CENTER) - - def show(self): - if self not in alive_windows: - raise ValueError("Window destroyed") - self._window.show() - - def close(self): - if hasattr(self, "_closing"): - return - self._closing = True - # Keep a reference to the widget in case will-close signal handler - # calls destroy() - old_window = self._window - self.emit('will-close') - old_window.hide() - del self._closing - - def destroy(self): - self.close() - self._window = None - alive_windows.discard(self) - - def is_active(self): - return self._window.is_active() - - def is_visible(self): - return self._window.props.visible - - def get_next_tab_focus(self, current, is_forward): - return None - - def set_content_widget(self, widget): - """Set the widget that will be drawn in the content area for this - window. - - It will be allocated the entire area of the widget, except the - space needed for the titlebar, frame and other decorations. - When the window is resized, content should also be resized. - """ - self._add_content_widget(widget) - widget._widget.show() - self.content_widget = widget - - def _add_content_widget(self, widget): - self._window.add(widget._widget) - - def get_content_widget(self, widget): - """Get the current content widget.""" - return self.content_widget - - def get_frame(self): - pos = self._window.get_position() - size = self._window.get_size() - return widgets.Rect(pos[0], pos[1], size[0], size[1]) - - def set_frame(self, x=None, y=None, width=None, height=None): - if x is not None or y is not None: - pos = self._window.get_position() - x = x if x is not None else pos[0] - y = y if y is not None else pos[1] - self._window.move(x, y) - - if width is not None or height is not None: - size = self._window.get_size() - width = width if width is not None else size[0] - height = height if height is not None else size[1] - self._window.resize(width, height) - - def get_monitor_geometry(self): - """Returns a Rect of the geometry of the monitor that this - window is currently on. - - :returns: Rect - """ - gtkwindow = self._window - gdkwindow = gtkwindow.window - screen = gtkwindow.get_screen() - - monitor = screen.get_monitor_at_window(gdkwindow) - return screen.get_monitor_geometry(monitor) - - def check_position_and_fix(self): - """This pulls the geometry of the monitor of the screen this - window is on as well as the position of the window. - - It then makes sure that the position y is greater than the - monitor geometry y. This makes sure that the titlebar of - the window is showing. - """ - gtkwindow = self._window - gdkwindow = gtkwindow.window - monitor_geom = self.get_monitor_geometry() - - frame_extents = gdkwindow.get_frame_extents() - position = gtkwindow.get_position() - - # if the frame is not visible, then we move the window so that - # it is - if frame_extents.y < monitor_geom.y: - gtkwindow.move(position[0], - monitor_geom.y + (position[1] - frame_extents.y)) - - - -class DialogWindow(Window): - def __init__(self, title, rect=None): - Window.__init__(self, title, rect) - self._window.set_resizable(False) - -class MainWindow(Window): - def __init__(self, title, rect): - Window.__init__(self, title, rect) - self.vbox = gtk.VBox() - self._window.add(self.vbox) - self.vbox.show() - self._add_app_menubar() - self.create_signal('save-dimensions') - self.create_signal('save-maximized') - self._window.connect('key-release-event', self.on_key_release) - self._window.connect('window-state-event', self.on_window_state_event) - self._window.connect('configure-event', self.on_configure_event) - - def _make_gtk_window(self): - return WrappedWindow() - - def on_delete_window(self, widget, event): - return True - - def on_configure_event(self, widget, event): - (x, y) = self._window.get_position() - (width, height) = self._window.get_size() - self.emit('save-dimensions', x, y, width, height) - - def on_window_state_event(self, widget, event): - maximized = bool( - event.new_window_state & gtk.gdk.WINDOW_STATE_MAXIMIZED) - self.emit('save-maximized', maximized) - - def on_key_release(self, widget, event): - if app.playback_manager.is_playing: - if gtk.gdk.keyval_name(event.keyval) in ('Right', 'Left', - 'Up', 'Down'): - return True - - def _add_app_menubar(self): - self.menubar = app.widgetapp.menubar - self.vbox.pack_start(self.menubar._widget, expand=False) - self.connect_menu_keyboard_shortcuts() - - def _add_content_widget(self, widget): - self.vbox.pack_start(widget._widget, expand=True) - - -class DialogBase(WindowBase): - def set_transient_for(self, window): - self._window.set_transient_for(window._window) - - def run(self): - running_dialogs.add(self) - try: - return self._run() - finally: - running_dialogs.remove(self) - self._window = None - - def _run(self): - """Run the dialog. Must be implemented by subclasses.""" - raise NotImplementedError() - - def destroy(self): - if self._window is not None: - self._window.response(gtk.RESPONSE_NONE) - # don't set self._window to None yet. We will unset it when we - # return from the _run() method - -class Dialog(DialogBase): - def __init__(self, title, description=None): - """Create a dialog.""" - DialogBase.__init__(self) - self.create_signal('open') - self.create_signal('close') - self.set_window(gtk.Dialog(title)) - self._window.set_default_size(425, -1) - self.extra_widget = None - self.buttons_to_add = [] - wrappermap.add(self._window, self) - self.description = description - - def build_content(self): - packing_vbox = layout.VBox(spacing=20) - packing_vbox._widget.set_border_width(6) - if self.description is not None: - label = gtk.Label(self.description) - label.set_line_wrap(True) - label.set_size_request(390, -1) - label.set_selectable(True) - packing_vbox._widget.pack_start(label) - if self.extra_widget: - packing_vbox._widget.pack_start(self.extra_widget._widget) - return packing_vbox - - def add_button(self, text): - from mvc.widgets import dialogs - _stock = { - dialogs.BUTTON_OK.text: gtk.STOCK_OK, - dialogs.BUTTON_CANCEL.text: gtk.STOCK_CANCEL, - dialogs.BUTTON_YES.text: gtk.STOCK_YES, - dialogs.BUTTON_NO.text: gtk.STOCK_NO, - dialogs.BUTTON_QUIT.text: gtk.STOCK_QUIT, - dialogs.BUTTON_REMOVE.text: gtk.STOCK_REMOVE, - dialogs.BUTTON_DELETE.text: gtk.STOCK_DELETE, - } - if text in _stock: - # store both the text and the stock ID - text = _stock[text], text - self.buttons_to_add.append(text) - - def pack_buttons(self): - # There's a couple tricky things here: - # 1) We need to add them in the reversed order we got them, since GTK - # lays them out left-to-right - # - # 2) We can't use 0 as a response-id. GTK only reserves positive - # response_ids for the user. - response_id = len(self.buttons_to_add) - for text in reversed(self.buttons_to_add): - label = None - if isinstance(text, tuple): # stock ID, text - text, label = text - button = self._window.add_button(text, response_id) - if label is not None: - button.set_label(label) - response_id -= 1 - self.buttons_to_add = [] - self._window.set_default_response(1) - - def _run(self): - self.pack_buttons() - packing_vbox = self.build_content() - self._window.vbox.pack_start(packing_vbox._widget, True, True) - self._window.show_all() - response = self._window.run() - self._window.hide() - if response == gtk.RESPONSE_DELETE_EVENT: - return -1 - else: - return response - 1 # response IDs started at 1 - - def set_extra_widget(self, widget): - self.extra_widget = widget - - def get_extra_widget(self): - return self.extra_widget - -class FileDialogBase(DialogBase): - def _run(self): - ret = self._window.run() - self._window.hide() - if ret == gtk.RESPONSE_OK: - self._files = self._window.get_filenames() - return 0 - -class FileOpenDialog(FileDialogBase): - def __init__(self, title): - FileDialogBase.__init__(self) - self._files = None - fcd = gtk.FileChooserDialog(title, - action=gtk.FILE_CHOOSER_ACTION_OPEN, - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - gtk.STOCK_OPEN, - gtk.RESPONSE_OK)) - - self.set_window(fcd) - - def set_filename(self, text): - self._window.set_filename(text) - - def set_select_multiple(self, value): - self._window.set_select_multiple(value) - - def add_filters(self, filters): - for name, ext_list in filters: - f = gtk.FileFilter() - f.set_name(name) - for mem in ext_list: - f.add_pattern('*.%s' % mem) - self._window.add_filter(f) - - f = gtk.FileFilter() - f.set_name(_('All files')) - f.add_pattern('*') - self._window.add_filter(f) - - def get_filenames(self): - return [unicode(f) for f in self._files] - - def get_filename(self): - if self._files is None: - # clicked Cancel - return None - else: - return unicode(self._files[0]) - - # provide a common interface for file chooser dialogs - get_path = get_filename - def set_path(self, path): - # set_filename puts the whole path in the filename field - self._window.set_current_folder(os.path.dirname(path)) - self._window.set_current_name(os.path.basename(path)) - -class FileSaveDialog(FileDialogBase): - def __init__(self, title): - FileDialogBase.__init__(self) - self._files = None - fcd = gtk.FileChooserDialog(title, - action=gtk.FILE_CHOOSER_ACTION_SAVE, - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - gtk.STOCK_SAVE, - gtk.RESPONSE_OK)) - self.set_window(fcd) - - def set_filename(self, text): - self._window.set_current_name(text) - - def get_filename(self): - if self._files is None: - # clicked Cancel - return None - else: - return unicode(self._files[0]) - - # provide a common interface for file chooser dialogs - get_path = get_filename - def set_path(self, path): - # set_filename puts the whole path in the filename field - self._window.set_current_folder(os.path.dirname(path)) - self._window.set_current_name(os.path.basename(path)) - -class DirectorySelectDialog(FileDialogBase): - def __init__(self, title): - FileDialogBase.__init__(self) - self._files = None - choose_str = 'Choose' - fcd = gtk.FileChooserDialog( - title, - action=gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER, - buttons=(gtk.STOCK_CANCEL, - gtk.RESPONSE_CANCEL, - choose_str, gtk.RESPONSE_OK)) - self.set_window(fcd) - - def set_directory(self, text): - self._window.set_filename(text) - - def get_directory(self): - if self._files is None: - # clicked Cancel - return None - else: - return unicode(self._files[0]) - - # provide a common interface for file chooser dialogs - get_path = get_directory - set_path = set_directory - -class AboutDialog(Dialog): - def __init__(self): - Dialog.__init__(self, "Libre Video Converter") -# _("About %(appname)s", -# {'appname': app.config.get(prefs.SHORT_APP_NAME)})) -# self.add_button(_("Close")) - self.add_button("Close") - self._window.set_has_separator(False) - - def build_content(self): - packing_vbox = layout.VBox(spacing=20) - #icon_pixbuf = gtk.gdk.pixbuf_new_from_file_at_size( - # resources.share_path('icons/hicolor/128x128/apps/miro.png'), - # 48, 48) - #packing_vbox._widget.pack_start(gtk.image_new_from_pixbuf(icon_pixbuf)) - #if app.config.get(prefs.APP_REVISION_NUM): - # version = "%s (%s)" % ( - # app.config.get(prefs.APP_VERSION), - # app.config.get(prefs.APP_REVISION_NUM)) - #else: - # version = "%s" % app.config.get(prefs.APP_VERSION) - version = '3.0' - #name_label = gtk.Label( - # '<span size="xx-large" weight="bold">%s %s</span>' % ( - # app.config.get(prefs.SHORT_APP_NAME), version)) - name_label = gtk.Label( - '<span size="xx-large" weight="bold">%s %s</span>' % ( - 'Libre Video Converter', version)) - name_label.set_use_markup(True) - packing_vbox._widget.pack_start(name_label) - copyright_text = 'Copyright (c) Jesus Eduardo (Heckyel) | 2017' - copyright_label = gtk.Label('<small>%s</small>' % copyright_text) - copyright_label.set_use_markup(True) - copyright_label.set_justify(gtk.JUSTIFY_CENTER) - packing_vbox._widget.pack_start(copyright_label) - - # FIXME - make the project url clickable - #packing_vbox._widget.pack_start( - # gtk.Label(app.config.get(prefs.PROJECT_URL))) - - #contributor_label = gtk.Label( - # _("Thank you to all the people who contributed to %(appname)s " - # "%(version)s:", - # {"appname": app.config.get(prefs.SHORT_APP_NAME), - # "version": app.config.get(prefs.APP_VERSION)})) - #contributor_label.set_justify(gtk.JUSTIFY_CENTER) - #packing_vbox._widget.pack_start(contributor_label) - - # get contributors, remove newlines and wrap it - #contributors = open(resources.path('CREDITS'), 'r').readlines() - #contributors = [c[2:].strip() - # for c in contributors if c.startswith("* ")] - #contributors = ", ".join(contributors) - - # show contributors - #contrib_buffer = gtk.TextBuffer() - #contrib_buffer.set_text(contributors) - - #contrib_view = gtk.TextView(contrib_buffer) - #contrib_view.set_editable(False) - #contrib_view.set_cursor_visible(False) - #contrib_view.set_wrap_mode(gtk.WRAP_WORD) - #contrib_window = gtk.ScrolledWindow() - #contrib_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_ALWAYS) - #contrib_window.add(contrib_view) - #contrib_window.set_size_request(-1, 100) - #packing_vbox._widget.pack_start(contrib_window) - - # FIXME - make the project url clickable - #donate_label = gtk.Label( - # _("To help fund continued %(appname)s development, visit the " - # "donation page at:", - # {"appname": app.config.get(prefs.SHORT_APP_NAME)})) - #donate_label.set_justify(gtk.JUSTIFY_CENTER) - #packing_vbox._widget.pack_start(donate_label) - - #packing_vbox._widget.pack_start( - # gtk.Label(app.config.get(prefs.DONATE_URL))) - return packing_vbox - - def on_contrib_link_event(self, texttag, widget, event, iter_): - if event.type == gtk.gdk.BUTTON_PRESS: - resources.open_url('https://notabug.org/heckyel/librevideoconverter') - -type_map = { - 0: gtk.MESSAGE_WARNING, - 1: gtk.MESSAGE_INFO, - 2: gtk.MESSAGE_ERROR -} - -class AlertDialog(DialogBase): - def __init__(self, title, description, alert_type): - DialogBase.__init__(self) - message_type = type_map.get(alert_type, gtk.MESSAGE_INFO) - self.set_window(gtk.MessageDialog(type=message_type, - message_format=description)) - self._window.set_title(title) - self.description = description - - def add_button(self, text): - self._window.add_button(_stock.get(text, text), 1) - self._window.set_default_response(1) - - def _run(self): - self._window.set_modal(False) - self._window.show_all() - response = self._window.run() - self._window.hide() - if response == gtk.RESPONSE_DELETE_EVENT: - return -1 - else: - # response IDs start at 1 - return response - 1 |