1# -*- coding: utf-8 -*- 2 3# Copyright (C) 2005 Osmo Salomaa 4# 5# This program is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18"""Dialog for selecting a subtitle file to save.""" 19 20import aeidon 21import gaupol 22import os 23 24from aeidon.i18n import _ 25from gi.repository import GObject 26from gi.repository import Gtk 27 28__all__ = ("SaveDialog",) 29 30 31class SaveDialog(Gtk.FileChooserDialog, gaupol.FileDialog): 32 33 """Dialog for selecting a subtitle file to save.""" 34 35 _widgets = ( 36 "encoding_combo", 37 "format_combo", 38 "framerate_combo", 39 "framerate_label", 40 "newline_combo", 41 ) 42 43 def __init__(self, parent, title, mode): 44 """Initialize a :class:`SaveDialog` instance.""" 45 GObject.GObject.__init__(self) 46 self._mode = mode 47 self._init_dialog(parent, title) 48 self._init_extra_widget() 49 self._init_filters() 50 self._init_format_combo() 51 self._init_encoding_combo() 52 self._init_newline_combo() 53 self._init_framerate_combo() 54 self._init_values() 55 56 def get_format(self): 57 """Return the selected format.""" 58 index = self._format_combo.get_active() 59 return aeidon.formats[index] 60 61 def get_framerate(self): 62 """Return the selected framerate.""" 63 index = self._framerate_combo.get_active() 64 return aeidon.framerates[index] 65 66 def get_newline(self): 67 """Return the selected newline.""" 68 index = self._newline_combo.get_active() 69 return aeidon.newlines[index] 70 71 def _init_dialog(self, parent, title): 72 """Initialize the dialog.""" 73 self.add_button(_("_Cancel"), Gtk.ResponseType.CANCEL) 74 self.add_button(_("_Save"), Gtk.ResponseType.OK) 75 self.set_default_response(Gtk.ResponseType.OK) 76 self.set_transient_for(parent) 77 self.set_title(title) 78 self.connect("response", self._on_response) 79 save_button = self.get_widget_for_response(Gtk.ResponseType.OK) 80 save_button.connect("event", self._on_save_button_event) 81 self.set_action(Gtk.FileChooserAction.SAVE) 82 self.set_do_overwrite_confirmation(True) 83 84 def _init_extra_widget(self): 85 """Initialize the extra widget from UI definition file.""" 86 ui_file_path = os.path.join(aeidon.DATA_DIR, "ui", "save-dialog.ui") 87 builder = Gtk.Builder() 88 builder.set_translation_domain("gaupol") 89 builder.add_from_file(ui_file_path) 90 builder.connect_signals(self) 91 for name in self._widgets: 92 widget = builder.get_object(name) 93 setattr(self, "_{}".format(name), widget) 94 vbox = gaupol.util.new_vbox(spacing=0) 95 main_vbox = builder.get_object("main_vbox") 96 main_vbox.get_parent().remove(main_vbox) 97 vbox.add(main_vbox) 98 vbox.show_all() 99 self.set_extra_widget(vbox) 100 101 def _init_format_combo(self): 102 """Initialize the format combo box.""" 103 store = Gtk.ListStore(str) 104 self._format_combo.set_model(store) 105 for name in (x.label for x in aeidon.formats): 106 if name == "SubRip": 107 # Mark the SubRip format as recommended, since 108 # it's the most common one and best supported, 109 # both in Gaupol and elsewhere. 110 name = _("{} (recommended)").format(name) 111 store.append((name,)) 112 view = self._format_combo.get_child() 113 path = gaupol.util.tree_row_to_path(0) 114 view.set_displayed_row(path) 115 renderer = Gtk.CellRendererText() 116 self._format_combo.pack_start(renderer, expand=True) 117 self._format_combo.add_attribute(renderer, "text", 0) 118 119 def _init_framerate_combo(self): 120 """Initialize the framerate combo box.""" 121 store = Gtk.ListStore(str) 122 self._framerate_combo.set_model(store) 123 for name in (x.label for x in aeidon.framerates): 124 store.append((name,)) 125 view = self._framerate_combo.get_child() 126 path = gaupol.util.tree_row_to_path(0) 127 view.set_displayed_row(path) 128 renderer = Gtk.CellRendererText() 129 self._framerate_combo.pack_start(renderer, expand=True) 130 self._framerate_combo.add_attribute(renderer, "text", 0) 131 132 def _init_newline_combo(self): 133 """Initialize the newline combo box.""" 134 store = Gtk.ListStore(str) 135 self._newline_combo.set_model(store) 136 for name in (x.label for x in aeidon.newlines): 137 store.append((name,)) 138 view = self._newline_combo.get_child() 139 path = gaupol.util.tree_row_to_path(0) 140 view.set_displayed_row(path) 141 renderer = Gtk.CellRendererText() 142 self._newline_combo.pack_start(renderer, expand=True) 143 self._newline_combo.add_attribute(renderer, "text", 0) 144 145 def _init_values(self): 146 """Initialize default values for widgets.""" 147 if os.path.isdir(gaupol.conf.file.directory): 148 self.set_current_folder(gaupol.conf.file.directory) 149 self.set_encoding(gaupol.conf.file.encoding) 150 self.set_format(gaupol.conf.file.format) 151 self.set_newline(gaupol.conf.file.newline) 152 self.set_framerate(gaupol.conf.editor.framerate) 153 self._framerate_combo.hide() 154 self._framerate_label.hide() 155 156 def _on_format_combo_changed(self, *args): 157 """Change the extension of the current filename.""" 158 format = self.get_format() 159 path = self.get_filename() 160 if path is not None: 161 dirname = os.path.dirname(path) 162 basename = os.path.basename(path) 163 if not path.endswith(format.extension): 164 basename = aeidon.util.replace_extension(basename, format) 165 path = os.path.join(dirname, basename) 166 self.unselect_filename(path) 167 self.set_current_name(basename) 168 self.set_filename(path) 169 visible = (format.mode != self._mode) 170 self._framerate_combo.set_visible(visible) 171 self._framerate_label.set_visible(visible) 172 173 def _on_response(self, dialog, response): 174 """Save default values for widgets.""" 175 directory = self.get_current_folder() 176 if directory is not None: 177 gaupol.conf.file.directory = directory 178 gaupol.conf.file.encoding = self.get_encoding() 179 gaupol.conf.file.format = self.get_format() 180 gaupol.conf.file.newline = self.get_newline() 181 gaupol.conf.editor.framerate = self.get_framerate() 182 if (response == Gtk.ResponseType.OK and 183 not self.get_filename().endswith(self.get_format().extension)): 184 # If the filename is lacking the extension, add it, stop this 185 # response and emit a new one so that overwrite confirmation gets 186 # called with the full filename. The filename extension might have 187 # already been added in self._on_save_button_event, but not 188 # necessarily if the user hit Enter on the keyboard. 189 self._format_combo.emit("changed") 190 gaupol.util.iterate_main() 191 self.stop_emission("response") 192 return self.response(Gtk.ResponseType.OK) 193 194 def _on_save_button_event(self, button, event): 195 """Ensure that the filename contains an extension.""" 196 # Add possibly lacking extension to the filename. 197 self._format_combo.emit("changed") 198 gaupol.util.iterate_main() 199 200 def set_format(self, format): 201 """Set the selected format.""" 202 if format is None: return 203 self._format_combo.set_active(format) 204 205 def set_framerate(self, framerate): 206 """Set the selected framerate.""" 207 if framerate is None: return 208 self._framerate_combo.set_active(framerate) 209 210 def set_name(self, path): 211 """Set the selected filename.""" 212 if os.path.isfile(path): 213 return self.set_filename(path) 214 return self.set_current_name(path) 215 216 def set_newline(self, newline): 217 """Set the selected newline.""" 218 if newline is None: return 219 self._newline_combo.set_active(newline) 220