aboutsummaryrefslogtreecommitdiffstats
path: root/lvc/widgets/gtk/window.py
diff options
context:
space:
mode:
authorJesús Eduardo <heckyel@hyperbola.info>2017-09-11 17:47:17 -0500
committerJesús Eduardo <heckyel@hyperbola.info>2017-09-11 17:47:17 -0500
commit14738704ede6dfa6ac79f362a9c1f7f40f470cdc (patch)
tree31c83bdd188ae7b64d7169974d6f066ccfe95367 /lvc/widgets/gtk/window.py
parenteb1896583afbbb622cadcde1a24e17173f61904f (diff)
downloadlibrevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.lz
librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.tar.xz
librevideoconverter-14738704ede6dfa6ac79f362a9c1f7f40f470cdc.zip
rename mvc at lvc
Diffstat (limited to 'lvc/widgets/gtk/window.py')
-rw-r--r--lvc/widgets/gtk/window.py708
1 files changed, 708 insertions, 0 deletions
diff --git a/lvc/widgets/gtk/window.py b/lvc/widgets/gtk/window.py
new file mode 100644
index 0000000..de912cc
--- /dev/null
+++ b/lvc/widgets/gtk/window.py
@@ -0,0 +1,708 @@
+# @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 lvc import resources
+from lvc 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("lvc-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 lvc.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 = '1.0.1'
+ #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