1# Copyright (C) 2011 Chris Dekter
2# Copyright (C) 2018, 2019 Thomas Hess <thomas.hess@udo.edu>
3
4# This program is free software: you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation, either version 3 of the License, or
7# (at your option) any later version.
8
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12# GNU General Public License for more details.
13
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16import subprocess
17
18from PyQt5.QtWidgets import QMessageBox
19
20from autokey import model
21from autokey.qtui import common as ui_common
22
23
24PROBLEM_MSG_PRIMARY = "Some problems were found"
25PROBLEM_MSG_SECONDARY = "{}\n\nYour changes have not been saved."
26
27
28# TODO: Once the port to Qt5 is done, set the editor placeholder text in the UI file to "Enter your phrase here."
29# TODO: Pure Qt4 QTextEdit does not support placeholder texts, so this functionality is currently unavailable.
30class PhrasePage(*ui_common.inherits_from_ui_file_with_name("phrasepage")):
31
32    def __init__(self):
33        super(PhrasePage, self).__init__()
34        self.setupUi(self)
35
36        self.initialising = True
37        self.current_phrase = None  # type: model.Phrase
38
39        for val in sorted(model.SEND_MODES.keys()):
40            self.sendModeCombo.addItem(val)
41        self.initialising = False
42
43    def load(self, phrase: model.Phrase):
44        self.current_phrase = phrase
45        self.phraseText.setPlainText(phrase.phrase)
46        self.showInTrayCheckbox.setChecked(phrase.show_in_tray_menu)
47
48        for k, v in model.SEND_MODES.items():
49            if v == phrase.sendMode:
50                self.sendModeCombo.setCurrentIndex(self.sendModeCombo.findText(k))
51                break
52
53        if self.is_new_item():
54            self.urlLabel.setEnabled(False)
55            self.urlLabel.setText("(Unsaved)")  # TODO: i18n
56        else:
57            ui_common.set_url_label(self.urlLabel, self.current_phrase.path)
58
59        # TODO - re-enable me if restoring predictive functionality
60        #self.predictCheckbox.setChecked(model.TriggerMode.PREDICTIVE in phrase.modes)
61
62        self.promptCheckbox.setChecked(phrase.prompt)
63        self.settingsWidget.load(phrase)
64
65    def save(self):
66        self.settingsWidget.save()
67        self.current_phrase.phrase = str(self.phraseText.toPlainText())
68        self.current_phrase.show_in_tray_menu = self.showInTrayCheckbox.isChecked()
69
70        self.current_phrase.sendMode = model.SEND_MODES[str(self.sendModeCombo.currentText())]
71
72        # TODO - re-enable me if restoring predictive functionality
73        #if self.predictCheckbox.isChecked():
74        #    self.currentPhrase.modes.append(model.TriggerMode.PREDICTIVE)
75
76        self.current_phrase.prompt = self.promptCheckbox.isChecked()
77
78        self.current_phrase.persist()
79        ui_common.set_url_label(self.urlLabel, self.current_phrase.path)
80        return False
81
82    def get_current_item(self):
83        """Returns the currently held item."""
84        return self.current_phrase
85
86    def set_item_title(self, title):
87        self.current_phrase.description = title
88
89    def rebuild_item_path(self):
90        self.current_phrase.rebuild_path()
91
92    def is_new_item(self):
93        return self.current_phrase.path is None
94
95    def reset(self):
96        self.load(self.current_phrase)
97
98    def validate(self):
99        errors = []
100
101        # Check phrase content
102        phrase = str(self.phraseText.toPlainText())
103        if ui_common.EMPTY_FIELD_REGEX.match(phrase):
104            errors.append("The phrase content can't be empty")  # TODO: i18n
105
106        # Check settings
107        errors += self.settingsWidget.validate()
108
109        if errors:
110            msg = PROBLEM_MSG_SECONDARY.format('\n'.join([str(e) for e in errors]))
111            QMessageBox.critical(self.window(), PROBLEM_MSG_PRIMARY, msg)
112
113        return not bool(errors)
114
115    def set_dirty(self):
116        self.window().set_dirty()
117
118    def undo(self):
119        self.phraseText.undo()
120
121    def redo(self):
122        self.phraseText.redo()
123
124    def insert_token(self, token):
125        self.phraseText.insertPlainText(token)
126
127    # --- Signal handlers
128    def on_phraseText_textChanged(self):
129        self.set_dirty()
130
131    def on_phraseText_undoAvailable(self, state):
132        self.window().set_undo_available(state)
133
134    def on_phraseText_redoAvailable(self, state):
135        self.window().set_redo_available(state)
136
137    def on_predictCheckbox_stateChanged(self, state):
138        self.set_dirty()
139
140    def on_promptCheckbox_stateChanged(self, state):
141        self.set_dirty()
142
143    def on_showInTrayCheckbox_stateChanged(self, state):
144        self.set_dirty()
145
146    def on_sendModeCombo_currentIndexChanged(self, index):
147        if not self.initialising:
148            self.set_dirty()
149
150    def on_urlLabel_leftClickedUrl(self, url=None):
151        if url:
152            subprocess.Popen(["/usr/bin/xdg-open", url])
153