1#
2# This file is part of the LibreOffice project.
3#
4# This Source Code Form is subject to the terms of the Mozilla Public
5# License, v. 2.0. If a copy of the MPL was not distributed with this
6# file, You can obtain one at http://mozilla.org/MPL/2.0/.
7#
8# This file incorporates work covered by the following license notice:
9#
10#   Licensed to the Apache Software Foundation (ASF) under one or more
11#   contributor license agreements. See the NOTICE file distributed
12#   with this work for additional information regarding copyright
13#   ownership. The ASF licenses this file to you under the Apache
14#   License, Version 2.0 (the "License"); you may not use this file
15#   except in compliance with the License. You may obtain a copy of
16#   the License at http://www.apache.org/licenses/LICENSE-2.0 .
17#
18import uno
19import traceback
20from abc import ABCMeta, abstractmethod
21from .UnoDialog2 import UnoDialog2, Desktop, PropertyNames, UIConsts, \
22    ItemListenerProcAdapter
23from ..common.HelpIds import HelpIds
24from ..common.FileAccess import FileAccess
25
26from com.sun.star.lang import NoSuchMethodException
27from com.sun.star.frame import TerminationVetoException
28from com.sun.star.awt.PushButtonType import HELP, STANDARD
29from com.sun.star.awt.FontWeight import BOLD
30
31import sys, os
32
33# imp is deprecated since Python v.3.4
34if sys.version_info >= (3,3):
35    from importlib.machinery import SourceFileLoader
36    SourceFileLoader('strings', os.path.join(os.path.dirname(__file__), '../common/strings.hrc')).load_module()
37else:
38    import imp
39    imp.load_source('strings', os.path.join(os.path.dirname(__file__), '../common/strings.hrc'))
40
41import strings
42
43class WizardDialog(UnoDialog2):
44
45    __metaclass__ = ABCMeta
46
47    __NEXT_ACTION_PERFORMED = "gotoNextAvailableStep"
48    __BACK_ACTION_PERFORMED = "gotoPreviousAvailableStep"
49    __FINISH_ACTION_PERFORMED = "finishWizard_1"
50    __CANCEL_ACTION_PERFORMED = "cancelWizard_1"
51    __HELP_ACTION_PERFORMED = None
52
53    '''
54    Creates a new instance of WizardDialog
55    the hid is used as following :
56    "HID:(hid)"   - the dialog
57    "HID:(hid+1)  - the help button
58    "HID:(hid+2)" - the back button
59    "HID:(hid+3)" - the next button
60    "HID:(hid+4)" - the create button
61    "HID:(hid+5)" - the cancel button
62    @param xMSF
63    @param hid_
64    '''
65
66    def __init__(self, xMSF, hid_):
67        super(WizardDialog,self).__init__(xMSF)
68        self.__hid = hid_
69        self.iButtonWidth = 50
70        self.nNewStep = 1
71        self.nOldStep = 1
72        self.nMaxStep = 1
73        self.bTerminateListenermustberemoved = True
74        self.oRoadmap = None
75        self.terminateListener = None
76
77    def activate(self):
78        try:
79            self.xUnoDialog.toFront()
80        except Exception:
81            pass
82            # do nothing;
83
84    def itemStateChanged(self, itemEvent):
85        try:
86            self.nNewStep = itemEvent.ItemId
87            self.nOldStep = int(self.xDialogModel.Step)
88            if self.nNewStep != self.nOldStep:
89                self.switchToStep()
90
91        except Exception:
92            traceback.print_exc()
93
94    def setDialogProperties(self, closeable, height, moveable, position_x,
95        position_Y, step, tabIndex, title, width):
96        uno.invoke(self.xDialogModel, "setPropertyValues",
97             ((PropertyNames.PROPERTY_CLOSEABLE,
98                PropertyNames.PROPERTY_HEIGHT,
99                PropertyNames.PROPERTY_MOVEABLE,
100                PropertyNames.PROPERTY_POSITION_X,
101                PropertyNames.PROPERTY_POSITION_Y,
102                PropertyNames.PROPERTY_STEP,
103                PropertyNames.PROPERTY_TABINDEX,
104                PropertyNames.PROPERTY_TITLE,
105                PropertyNames.PROPERTY_WIDTH),
106             (closeable, height, moveable, position_x, position_Y, step,
107                tabIndex, title, width)))
108
109    def setRoadmapInteractive(self, _bInteractive):
110        self.oRoadmap.Activated = _bInteractive
111
112    def setRoadmapComplete(self, bComplete):
113        self.oRoadmap.Complete = bComplete
114
115    def isRoadmapComplete(self):
116        try:
117            return bool(self.oRoadmap.Complete)
118        except Exception:
119            traceback.print_exc()
120            return False
121
122    def setCurrentRoadmapItemID(self, ID):
123        if self.oRoadmap is not None:
124            nCurItemID = self.getCurrentRoadmapItemID()
125            if nCurItemID != ID:
126                self.oRoadmap.CurrentItemID = ID
127
128    def getCurrentRoadmapItemID(self):
129        try:
130            return int(self.oRoadmap.CurrentItemID)
131        except Exception:
132            traceback.print_exc()
133            return -1
134
135
136    def initializePaths(self):
137        xPropertySet = \
138            self.xMSF.createInstance("com.sun.star.util.PathSettings")
139        self.sUserTemplatePath = \
140            xPropertySet.getPropertyValue("Template_writable")
141        myFA = FileAccess(self.xMSF)
142        aInternalPaths = xPropertySet.getPropertyValue("Template_internal")
143        self.sTemplatePath = ""
144        for path in aInternalPaths:
145            if myFA.exists(path + "/wizard", False):
146                self.sTemplatePath = path
147                break
148        if self.sTemplatePath == "":
149            raise Exception("could not find wizard templates")
150
151    def addRoadmap(self):
152        try:
153            iDialogHeight = self.xDialogModel.Height
154            # the roadmap control has got no real TabIndex ever
155            # that is not correct, but changing this would need time,
156            # so it is used without TabIndex as before
157
158            xRoadmapControl = self.insertControlModel(
159                "com.sun.star.awt.UnoControlRoadmapModel",
160                "rdmNavi",
161                (PropertyNames.PROPERTY_HEIGHT,
162                    PropertyNames.PROPERTY_POSITION_X,
163                    PropertyNames.PROPERTY_POSITION_Y,
164                    PropertyNames.PROPERTY_STEP,
165                    PropertyNames.PROPERTY_TABINDEX, "Tabstop",
166                    PropertyNames.PROPERTY_WIDTH),
167                ((iDialogHeight - 26), 0, 0, 0,
168                    0, True, 85))
169            self.oRoadmap = xRoadmapControl.Model
170            method = getattr(self, "itemStateChanged")
171            xRoadmapControl.addItemListener(
172                ItemListenerProcAdapter(method))
173
174            self.oRoadmap.Text = strings.RID_COMMON_START_16
175        except NoSuchMethodException:
176            from com.sun.star.awt.VclWindowPeerAttribute import OK
177            from .SystemDialog import SystemDialog
178            sError = "The files required could not be found.\n" + \
179                "Please start the LibreOffice Setup and choose 'Repair'."
180            SystemDialog.showMessageBox(super().xMSF, "ErrorBox", OK, sError)
181        except Exception:
182            traceback.print_exc()
183
184    def getRoadmapItemByID(self, _ID):
185        try:
186            getByIndex = self.oRoadmap.getByIndex
187            for i in list(range(self.oRoadmap.Count)):
188                CurRoadmapItem = getByIndex(i)
189                CurID = int(CurRoadmapItem.ID)
190                if CurID == _ID:
191                    return CurRoadmapItem
192
193            return None
194        except Exception:
195            traceback.print_exc()
196            return None
197
198    def switchToStep(self,_nOldStep=None, _nNewStep=None):
199        if _nOldStep is not None and _nNewStep is not None:
200            self.nOldStep = _nOldStep
201            self.nNewStep = _nNewStep
202
203        self.leaveStep(self.nOldStep, self.nNewStep)
204        if self.nNewStep != self.nOldStep:
205            if self.nNewStep == self.nMaxStep:
206                self.xDialogModel.btnWizardNext.DefaultButton = False
207                self.xDialogModel.btnWizardFinish.DefaultButton = True
208            else:
209                self.xDialogModel.btnWizardNext.DefaultButton = True
210                self.xDialogModel.btnWizardFinish.DefaultButton = False
211
212            self.changeToStep(self.nNewStep)
213            self.enterStep(self.nOldStep, self.nNewStep)
214            return True
215
216        return False
217
218    @abstractmethod
219    def leaveStep(self, nOldStep, nNewStep):
220        pass
221
222    @abstractmethod
223    def enterStep(self, nOldStep, nNewStep):
224        pass
225
226    def changeToStep(self, nNewStep):
227        self.xDialogModel.Step = nNewStep
228        self.setCurrentRoadmapItemID(nNewStep)
229        self.enableNextButton(self.getNextAvailableStep() > 0)
230        self.enableBackButton(nNewStep != 1)
231
232    def drawNaviBar(self):
233        try:
234            curtabindex = UIConsts.SOFIRSTWIZARDNAVITABINDEX
235            iButtonWidth = self.iButtonWidth
236            iButtonHeight = 14
237            iCurStep = 0
238            iDialogHeight = self.xDialogModel.Height
239            iDialogWidth = self.xDialogModel.Width
240            iHelpPosX = 8
241            iBtnPosY = iDialogHeight - iButtonHeight - 6
242            iCancelPosX = iDialogWidth - self.iButtonWidth - 6
243            iFinishPosX = iCancelPosX - 6 - self.iButtonWidth
244            iNextPosX = iFinishPosX - 6 - self.iButtonWidth
245            iBackPosX = iNextPosX - 3 - self.iButtonWidth
246            self.insertControlModel(
247                "com.sun.star.awt.UnoControlFixedLineModel",
248                "lnNaviSep",
249                (PropertyNames.PROPERTY_HEIGHT, "Orientation",
250                    PropertyNames.PROPERTY_POSITION_X,
251                    PropertyNames.PROPERTY_POSITION_Y,
252                    PropertyNames.PROPERTY_STEP,
253                    PropertyNames.PROPERTY_WIDTH),
254                (1, 0, 0, iDialogHeight - 26, iCurStep, iDialogWidth))
255            self.insertControlModel(
256                "com.sun.star.awt.UnoControlFixedLineModel",
257                "lnRoadSep",
258                (PropertyNames.PROPERTY_HEIGHT,
259                    "Orientation",
260                    PropertyNames.PROPERTY_POSITION_X,
261                    PropertyNames.PROPERTY_POSITION_Y,
262                    PropertyNames.PROPERTY_STEP,
263                    PropertyNames.PROPERTY_WIDTH),
264                (iBtnPosY - 6, 1, 85, 0, iCurStep, 1))
265            propNames = (PropertyNames.PROPERTY_ENABLED,
266                PropertyNames.PROPERTY_HEIGHT,
267                PropertyNames.PROPERTY_HELPURL,
268                PropertyNames.PROPERTY_LABEL,
269                PropertyNames.PROPERTY_POSITION_X,
270                PropertyNames.PROPERTY_POSITION_Y,
271                "PushButtonType",
272                PropertyNames.PROPERTY_STEP,
273                PropertyNames.PROPERTY_TABINDEX,
274                PropertyNames.PROPERTY_WIDTH)
275            self.xDialogModel.HelpURL = HelpIds.getHelpIdString(self.__hid)
276            self.insertButton("btnWizardHelp",
277                WizardDialog.__HELP_ACTION_PERFORMED,
278                (PropertyNames.PROPERTY_ENABLED,
279                    PropertyNames.PROPERTY_HEIGHT,
280                    PropertyNames.PROPERTY_LABEL,
281                    PropertyNames.PROPERTY_POSITION_X,
282                    PropertyNames.PROPERTY_POSITION_Y,
283                    "PushButtonType",
284                    PropertyNames.PROPERTY_STEP,
285                    PropertyNames.PROPERTY_TABINDEX,
286                    PropertyNames.PROPERTY_WIDTH),
287                (True, iButtonHeight,
288                    strings.RID_COMMON_START_15,
289                    iHelpPosX, iBtnPosY,
290                    uno.Any("short",HELP), iCurStep,
291                    uno.Any("short",(curtabindex + 1)), iButtonWidth), self)
292            self.insertButton("btnWizardBack",
293                WizardDialog.__BACK_ACTION_PERFORMED, propNames,
294                (False, iButtonHeight, HelpIds.getHelpIdString(self.__hid + 2),
295                    strings.RID_COMMON_START_13,
296                    iBackPosX, iBtnPosY, uno.Any("short",STANDARD), iCurStep,
297                    uno.Any("short",(curtabindex + 1)), iButtonWidth), self)
298            self.insertButton("btnWizardNext",
299                WizardDialog.__NEXT_ACTION_PERFORMED, propNames,
300                (True, iButtonHeight, HelpIds.getHelpIdString(self.__hid + 3),
301                    strings.RID_COMMON_START_14,
302                    iNextPosX, iBtnPosY, uno.Any("short",STANDARD), iCurStep,
303                    uno.Any("short",(curtabindex + 1)), iButtonWidth), self)
304            self.insertButton("btnWizardFinish",
305                WizardDialog.__FINISH_ACTION_PERFORMED, propNames,
306                (True, iButtonHeight, HelpIds.getHelpIdString(self.__hid + 4),
307                    strings.RID_COMMON_START_12,
308                        iFinishPosX, iBtnPosY, uno.Any("short",STANDARD),
309                        iCurStep,
310                        uno.Any("short",(curtabindex + 1)),
311                        iButtonWidth), self)
312            self.insertButton("btnWizardCancel",
313                WizardDialog.__CANCEL_ACTION_PERFORMED, propNames,
314                (True, iButtonHeight, HelpIds.getHelpIdString(self.__hid + 5),
315                    strings.RID_COMMON_START_11,
316                    iCancelPosX, iBtnPosY, uno.Any("short",STANDARD), iCurStep,
317                    uno.Any("short",(curtabindex + 1)),
318                    iButtonWidth), self)
319            self.xDialogModel.btnWizardNext.DefaultButton = True
320        except Exception:
321            traceback.print_exc()
322
323    def insertRoadMapItems(self, items, enabled):
324        for index, item in enumerate(items):
325            try:
326                oRoadmapItem = self.oRoadmap.createInstance()
327                oRoadmapItem.Label = item
328                oRoadmapItem.Enabled = enabled[index]
329                oRoadmapItem.ID = index + 1
330                self.oRoadmap.insertByIndex(index, oRoadmapItem)
331            except Exception:
332                traceback.print_exc()
333
334    def enableBackButton(self, enabled):
335        self.xDialogModel.btnWizardBack.Enabled = enabled
336
337    def enableNextButton(self, enabled):
338        self.xDialogModel.btnWizardNext.Enabled = enabled
339
340    def enableFinishButton(self, enabled):
341        self.xDialogModel.btnWizardFinish.Enabled = enabled
342
343    def isStepEnabled(self, _nStep):
344        try:
345            xRoadmapItem = self.getRoadmapItemByID(_nStep)
346            # Todo: In this case an exception should be thrown
347            if xRoadmapItem is None:
348                return False
349            bIsEnabled = bool(xRoadmapItem.Enabled)
350            return bIsEnabled
351        except Exception:
352            traceback.print_exc()
353            return False
354
355    def gotoPreviousAvailableStep(self):
356        try:
357            if self.nNewStep > 1:
358                self.nOldStep = self.nNewStep
359                self.nNewStep -= 1
360                while self.nNewStep > 0:
361                    bIsEnabled = self.isStepEnabled(self.nNewStep)
362                    if bIsEnabled:
363                        break;
364
365                    self.nNewStep -= 1
366                if (self.nNewStep == 0):
367                    self.nNewStep = self.nOldStep
368                self.switchToStep()
369        except Exception:
370            traceback.print_exc()
371
372    def getNextAvailableStep(self):
373        if self.isRoadmapComplete():
374            i = self.nNewStep + 1
375            while i <= self.nMaxStep:
376                if self.isStepEnabled(i):
377                    return i
378                i += 1
379        return -1
380
381    def gotoNextAvailableStep(self):
382        try:
383            self.nOldStep = self.nNewStep
384            self.nNewStep = self.getNextAvailableStep()
385            if self.nNewStep > -1:
386                self.switchToStep()
387        except Exception:
388            traceback.print_exc()
389
390    @abstractmethod
391    def finishWizard(self):
392        pass
393
394    def finishWizard_1(self):
395        '''This function will call
396        if the finish button is pressed on the UI'''
397        try:
398            self.enableFinishButton(False)
399            success = False
400            try:
401                success = self.finishWizard()
402            finally:
403                if not success:
404                    self.enableFinishButton(True)
405
406            if success:
407                self.removeTerminateListener()
408        except Exception:
409             traceback.print_exc()
410
411    def getCurrentStep(self):
412        try:
413            return int(self.xDialogModel.Step)
414        except Exception:
415            traceback.print_exc()
416            return -1
417
418    def cancelWizard(self):
419        #can be overwritten by extending class
420        self.xUnoDialog.endExecute()
421
422    def removeTerminateListener(self):
423        if self.bTerminateListenermustberemoved:
424            Desktop.getDesktop(self.xMSF).removeTerminateListener(self.terminateListener)
425            self.bTerminateListenermustberemoved = False
426
427    '''
428    called by the cancel button and
429    by the window hidden event.
430    if this method was not called before,
431    perform a cancel.
432    '''
433
434    def cancelWizard_1(self):
435        try:
436            self.cancelWizard()
437            self.removeTerminateListener()
438        except Exception:
439            traceback.print_exc()
440
441    def windowHidden(self):
442        self.cancelWizard_1()
443
444    def queryTermination(self):
445        self.activate()
446        raise TerminationVetoException()
447
448    def disposing(self, arg0):
449        self.cancelWizard_1()
450
451    def optCreateFromTemplateItemChanged(self):
452        self.bEditTemplate = False
453
454    def optMakeChangesItemChanged(self):
455        self.bEditTemplate = True
456