1# ----------------------------------------------------------------------
2# $Id: qrcontroller.py 2640 2014-08-12 02:04:01Z thomas-sturm $
3# ----------------------------------------------------------------------
4# (c) 2009 T. Sturm, 2010 T. Sturm, C. Zengler, 2011-2014 T. Sturm
5# ----------------------------------------------------------------------
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions
8# are met:
9#
10#    * Redistributions of source code must retain the relevant
11#      copyright notice, this list of conditions and the following
12#      disclaimer.
13#    * Redistributions in binary form must reproduce the above
14#      copyright notice, this list of conditions and the following
15#      disclaimer in the documentation and/or other materials provided
16#      with the distribution.
17#
18# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22# OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29#
30
31from PySide.QtCore import QObject
32from PySide.QtCore import Signal
33
34from qrlogging import fontLogger
35from qrlogging import signalLogger
36from qrlogging import traceLogger
37
38from qrmodel import QtReduceComputation
39from qrmodel import QtReduceModel
40
41from qrview import QtReduceFrameView
42from qrview import SubCell
43
44from types import BooleanType
45from types import StringType
46
47
48class QtReduceController(QObject):
49    endComputation = Signal(object)
50    acceptAbort = Signal(BooleanType)
51    fileNameChanged = Signal(StringType)
52    modified = Signal(BooleanType)
53    startComputation = Signal(object)
54
55    def __init__(self,parent=None):
56        super(QtReduceController,self).__init__(parent)
57
58        self.mainWindow = parent
59
60        self.setFileName('')
61
62        self.model = QtReduceModel(self)
63        self.view = QtReduceFrameView(parent)
64
65        self.view.computationRequest.connect(self.computationRequestV)
66        self.view.modified.connect(self.modifiedV)
67
68        self.model.dataChanged.connect(self.dataChangedM)
69        self.model.endComputation.connect(self.endComputationM)
70        self.model.rowsInserted.connect(self.rowsInsertedM)
71        self.model.rowsRemoved.connect(self.rowsRemovedM)
72        self.model.startComputation.connect(self.startComputationM)
73        self.updatingModel = False
74
75        self.model.insertRow(0)
76
77    def abortComputation(self):
78        self.model.abortComputation()
79
80    def computationRequestV(self,row):
81        signalLogger.debug("catching row = %d" % row)
82        if not self.view.input(row):
83            return
84        index = self.model.index(row,0)
85        computation = self.model.data(index,QtReduceModel.RawDataRole)
86        computation.command = self.view.input(row)
87        self.model.setData(index,computation,QtReduceModel.RawDataRole)
88        self.model.compute(row)
89
90    def evaluateAll(self):
91        for row in range(self.view.rowCount()):
92            self.computationRequestV(row)
93
94    def evaluateSelection(self):
95        None
96
97    def dataChangedM(self,topLeft,bottomRight):
98        if self.updatingModel:
99            return
100        start = topLeft.row()
101        end = bottomRight.row()
102        signalLogger.debug("start = %d, end = %d" % (start,end))
103        for row in range(start,end+1):
104            index = self.model.index(row,0)
105            computation = self.model.data(index,QtReduceModel.RawDataRole)
106            self.setCell(row,computation)
107            if computation.status in [QtReduceComputation.Error,
108                                      QtReduceComputation.Aborted]:
109                return
110        if row < self.model.rowCount() - 1:
111            self.view.gotoRow(row + 1)
112
113    def setCell(self,row,computation):
114        self.view.setCell(row,
115                          computation.c1,
116                          computation.command,
117                          computation.c2,
118                          computation.result,
119                          computation.c3)
120        s = computation.status
121        if s == QtReduceComputation.NotEvaluated:
122            self.view.setNotEvaluated(row)
123        elif s == QtReduceComputation.Evaluated:
124            if computation.result:
125                result = computation.result
126                if not computation.symbolic:
127                    result = self.renderResult(result)
128                self.view.setResult(row,result)
129            else:
130                self.view.setNoResult(row)
131        elif s == QtReduceComputation.Error:
132            self.view.setError(row,computation.errorText)
133        elif s == QtReduceComputation.Aborted:
134            self.view.setAborted(row)
135        else:
136            traceLogger.debug("Bad status: %d" % s)
137
138    def deleteOutput(self):
139        self.model.deleteOutput()
140
141    def deleteRowOrPreviousRow(self):
142        row = self.view.rowOrPreviousRow()
143        if row >= 0:
144            self.model.removeRow(row)
145        if self.model.rowCount() == 0:
146            self.model.insertRow(0)
147
148    def endComputationM(self,computation):
149        signalLogger.debug("catching computation.statCounter = %s,"
150                           "status=%s" %
151                           (computation.statCounter,computation.status))
152        self.view.setReadOnly(False)
153        self.acceptAbort.emit(False)
154        self.endComputation.emit(computation)
155
156    def fileName(self):
157        return self.__fileName
158
159    def insertAbove(self):
160        if self.view.atEnd():
161            self.model.insertRow(self.model.rowCount())
162            return
163        row = self.view.rowOrNextRow()
164        if row < 0:
165            traceLogger.critical("unxepected row %s" % row)
166        self.model.insertRow(row)
167        self.view.gotoRow(row)
168
169    def insertBelow(self):
170        if self.view.atStart():
171            self.model.insertRow(0)
172            return
173        row = self.view.rowOrPreviousRow()
174        if row < 0:
175            traceLogger.critical("unxepected row %s" % row)
176        self.model.insertRow(row + 1)
177        self.view.gotoRow(row + 1)
178
179    def modifiedV(self):
180        self.updatingModel = True
181        sc = self.view.subCell()
182        if sc.type != SubCell.Root:
183            row = sc.row
184            index = self.model.index(row,0)
185            computation = self.model.data(index,QtReduceModel.RawDataRole)
186            if sc.type == SubCell.Input:
187                computation.command = sc.content
188            elif sc.type == SubCell.C1:
189                computation.c1 = sc.content
190            elif sc.type == SubCell.C2:
191                computation.c2 = sc.content
192            elif sc.type == SubCell.C3:
193                computation.c3 = sc.content
194            self.model.setData(index,computation,QtReduceModel.RawDataRole)
195        self.modified.emit(True)
196        self.updatingModel = False
197
198    def open(self,fileName):
199        success = self.model.open(fileName)
200        if success:
201            self.view.gotoRow(self.model.rowCount() - 1)
202            self.modified.emit(False)
203            self.setFileName(fileName)
204            self.parent().showMessage(self.tr("Read ") + fileName)
205        return
206
207    def renderResult(self,result):
208        traceLogger.debug("result=%s" % result)
209        if result.strip("\\0123456789 ") == '':
210            return result.replace('\\ ','\\\n')
211        command = "qr_render(" + result.rstrip("$") + ");"
212        answer = self.model.reduce.reduce.compute(command)
213        rendered = answer['pretext']
214        if rendered:
215            rendered = rendered.strip("\n")
216        traceLogger.debug("rendered=%s" % rendered)
217        return rendered
218
219    def rowsInsertedM(self,parent,start,end):
220        traceLogger.debug("start=%d, end=%d" % (start, end))
221        self.view.insertRows(start,end)
222
223    def rowsRemovedM(self,parent,start,end):
224        traceLogger.debug("start=%d, end=%d" % (start, end))
225        self.view.removeRows(start,end)
226
227    def save(self):
228        if not self.__fileName:
229            traceLogger.critical('empty file name')
230        self.__saveAs(self.__fileName)
231
232    def saveAs(self,fileName):
233        self.__saveAs(fileName)
234        self.setFileName(fileName)
235
236    def setFileName(self,name):
237        self.__fileName = name
238        self.fileNameChanged.emit(name)
239
240    def startComputationM(self,computation):
241        signalLogger.debug("catching computation.command =  %s" %
242                           computation.command)
243        self.view.setReadOnly(True)
244        self.acceptAbort.emit(True)
245        self.startComputation.emit(computation)
246
247    def __saveAs(self,fileName):
248        self.model.save(fileName)
249        self.modified.emit(False)
250        self.parent().showMessage(self.tr("Wrote ") + fileName)
251