← Back to team overview

openlp-core team mailing list archive

[Merge] lp:~manndarryl/openlp/thememanager-margins into lp:openlp

 

Darryl Mann has proposed merging lp:~manndarryl/openlp/thememanager-margins into lp:openlp.

Requested reviews:
  OpenLP Core (openlp-core)

For more details, see:
https://code.launchpad.net/~manndarryl/openlp/thememanager-margins/+merge/239789

Added to Theme Manager the option to use margins to set the output area position.  By using margins, the same theme can be used on computers with different output screen sizes.
-- 
https://code.launchpad.net/~manndarryl/openlp/thememanager-margins/+merge/239789
Your team OpenLP Core is requested to review the proposed merge of lp:~manndarryl/openlp/thememanager-margins into lp:openlp.
=== modified file 'openlp/core/lib/json/theme.json'
--- openlp/core/lib/json/theme.json	2013-10-18 18:10:47 +0000
+++ openlp/core/lib/json/theme.json	2014-10-28 03:51:18 +0000
@@ -16,30 +16,38 @@
     "font": {
         "footer": {
             "bold": false,
+            "bottom": 0,
             "color": "#FFFFFF",
             "height": 78,
             "italics": false,
+            "left": 0,
             "line_adjustment": 0,
+            "margin": "",
             "location": "",
             "name": "Arial",
             "outline": false,
             "outline_color": "#000000",
             "outline_size": 2,
             "override": false,
+            "right": 0,
             "shadow": true,
             "shadow_color": "#000000",
             "shadow_size": 5,
             "size": 12,
+            "top": 0,
             "width": 1004,
             "x": 10,
             "y": 690
             },
         "main": {
             "bold": false,
+            "bottom": 0,
             "color": "#FFFFFF",
             "height": 690,
             "italics": false,
+            "left": 0,
             "line_adjustment": 0,
+            "margin": "",
             "location": "",
             "name": "Arial",
             "outline": false,
@@ -50,6 +58,8 @@
             "shadow_color": "#000000",
             "shadow_size": 5,
             "size": 40,
+            "right": 0,
+            "top": 0,
             "width": 1004,
             "x": 10,
             "y": 10

=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py	2014-07-02 18:13:23 +0000
+++ openlp/core/lib/renderer.py	2014-10-28 03:51:18 +0000
@@ -326,11 +326,18 @@
 
         :param theme_data: The theme information
         """
-        if not theme_data.font_main_override:
-            return QtCore.QRect(10, 0, self.width - 20, self.footer_start)
-        else:
+        font_footer_top = self.get_footer_top(theme_data)
+        if theme_data.font_main_override == 0:
+            return QtCore.QRect(10, 0, self.width - 20, font_footer_top)
+        elif theme_data.font_main_override == 1:
             return QtCore.QRect(theme_data.font_main_x, theme_data.font_main_y,
-                                theme_data.font_main_width - 1, theme_data.font_main_height - 1)
+                                theme_data.font_main_width - 1, font_footer_top - 1)
+        elif theme_data.font_main_override == 3:
+            main_width = self.width - theme_data.font_main_left - theme_data.font_main_right - 1
+            main_height = font_footer_top - theme_data.font_main_top - theme_data.font_main_bottom - 1
+            return QtCore.QRect(theme_data.font_main_left,theme_data.font_main_top, main_width, main_height)
+        else:
+            return QtCore.QRect(20, 20, self.width - 40, font_footer_top - 40)
 
     def get_footer_rectangle(self, theme_data):
         """
@@ -338,12 +345,34 @@
 
         :param theme_data: The theme data.
         """
-        if not theme_data.font_footer_override:
-            return QtCore.QRect(10, self.footer_start, self.width - 20, self.height - self.footer_start)
-        else:
+        font_footer_top = self.get_footer_top(theme_data)
+        if theme_data.font_footer_override == 0:
+            return QtCore.QRect(10, font_footer_top, self.width - 20, self.height - font_footer_top)
+        elif theme_data.font_footer_override == 1:
             return QtCore.QRect(theme_data.font_footer_x,
                                 theme_data.font_footer_y, theme_data.font_footer_width - 1,
                                 theme_data.font_footer_height - 1)
+        elif theme_data.font_footer_override == 3:
+            return QtCore.QRect(theme_data.font_footer_left, font_footer_top,
+                                self.width - theme_data.font_footer_left - theme_data.font_footer_right - 1,
+                                theme_data.font_footer_height - 1)
+        else:
+            return QtCore.QRect(20, font_footer_top, self.width - 40, theme_data.font_footer_height - 1)
+
+    def get_footer_top(self, theme_data):
+        """
+        Calculates the footer top coordinate
+
+        :param theme_data: The theme information
+        """
+        if theme_data.font_footer_override == 0:
+            return self.footer_start
+        elif theme_data.font_footer_override == 1:
+            return theme_data.font_footer_y
+        elif theme_data.font_footer_override == 3:
+            return self.height - theme_data.font_footer_height - theme_data.font_footer_bottom
+        else:
+            return self.height - 78 - 20
 
     def _set_text_rectangle(self, theme_data, rect_main, rect_footer):
         """

=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py	2014-03-17 19:05:55 +0000
+++ openlp/core/lib/theme.py	2014-10-28 03:51:18 +0000
@@ -147,10 +147,10 @@
     Names = ['top', 'middle', 'bottom']
 
 
-BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition']
+BOOLEAN_LIST = ['bold', 'italics', 'outline', 'shadow', 'slide_transition']
 
-INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'shadow_size', 'outline_size',
-                'horizontal_align', 'vertical_align', 'wrap_style']
+INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'left', 'right', 'top', 'bottom',
+                'shadow_size', 'outline_size', 'horizontal_align', 'vertical_align', 'wrap_style', 'override']
 
 
 class ThemeXML(object):
@@ -264,7 +264,8 @@
 
     def add_font(self, name, color, size, override, fonttype='main', bold='False', italics='False',
                  line_adjustment=0, xpos=0, ypos=0, width=0, height=0, outline='False', outline_color='#ffffff',
-                 outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5):
+                 outline_pixel=2, shadow='False', shadow_color='#ffffff', shadow_pixel=5,
+                 left=0, right=0, top=0, bottom=0):
         """
         Add a Font.
 
@@ -302,9 +303,22 @@
         self.child_element(background, 'italics', str(italics))
         # Create indentation name element
         self.child_element(background, 'line_adjustment', str(line_adjustment))
+        # Create Margin element
+        element = self.theme_xml.createElement('margin')
+        if override > 1:
+            element.setAttribute('override', str(override))
+        element.setAttribute('left', str(left))
+        element.setAttribute('right', str(right))
+        element.setAttribute('bottom', str(bottom))
+        if fonttype == 'footer':
+            element.setAttribute('height', str(height))
+        else:
+            element.setAttribute('top', str(top))
+        background.appendChild(element)
         # Create Location element
         element = self.theme_xml.createElement('location')
-        element.setAttribute('override', str(override))
+        if override < 2:
+            element.setAttribute('override', str(override))
         element.setAttribute('x', str(xpos))
         element.setAttribute('y', str(ypos))
         element.setAttribute('width', str(width))
@@ -469,6 +483,9 @@
                 value = True
         if element == 'proportion':
             element = 'size'
+        if element == 'override':
+            if value == 'True' or value == 'False':
+                value = int(value == 'True')
         return False, master, element, value
 
     def _create_attr(self, master, element, value):
@@ -538,7 +555,11 @@
             self.font_main_outline_size,
             self.font_main_shadow,
             self.font_main_shadow_color,
-            self.font_main_shadow_size
+            self.font_main_shadow_size,
+            self.font_main_left,
+            self.font_main_right,
+            self.font_main_top,
+            self.font_main_bottom
         )
         self.add_font(
             self.font_footer_name,
@@ -557,7 +578,11 @@
             self.font_footer_outline_size,
             self.font_footer_shadow,
             self.font_footer_shadow_color,
-            self.font_footer_shadow_size
+            self.font_footer_shadow_size,
+            self.font_footer_left,
+            self.font_footer_right,
+            self.font_footer_top,
+            self.font_footer_bottom
         )
         self.add_display(
             self.display_horizontal_align,

=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py	2014-09-04 20:25:23 +0000
+++ openlp/core/ui/themeform.py	2014-10-28 03:51:18 +0000
@@ -78,8 +78,10 @@
         self.shadow_check_box.stateChanged.connect(self.on_shadow_check_check_box_state_changed)
         self.footer_color_button.clicked.connect(self.on_footer_color_button_clicked)
         self.customButtonClicked.connect(self.on_custom_1_button_clicked)
-        self.main_position_check_box.stateChanged.connect(self.on_main_position_check_box_state_changed)
-        self.footer_position_check_box.stateChanged.connect(self.on_footer_position_check_box_state_changed)
+        self.main_position_combo_box.currentIndexChanged.connect(
+            self.on_main_position_combo_box_current_index_changed)
+        self.footer_position_combo_box.currentIndexChanged.connect(
+            self.on_footer_position_combo_box_current_index_changed)
         self.currentIdChanged.connect(self.on_current_id_changed)
         Registry().register_function('theme_line_count', self.update_lines_text)
         self.main_size_spin_box.valueChanged.connect(self.calculate_lines)
@@ -124,13 +126,22 @@
         self.main_area_page.registerField('shadow_color_button', self.shadow_color_button)
         self.main_area_page.registerField('shadow_size_spin_box', self.shadow_size_spin_box)
         self.main_area_page.registerField('footer_size_spin_box', self.footer_size_spin_box)
+        self.area_position_page.registerField('main_position_override', self.main_position_combo_box)
         self.area_position_page.registerField('main_position_x', self.main_x_spin_box)
         self.area_position_page.registerField('main_position_y', self.main_y_spin_box)
         self.area_position_page.registerField('main_position_width', self.main_width_spin_box)
         self.area_position_page.registerField('main_position_height', self.main_height_spin_box)
+        self.area_position_page.registerField('main_position_left', self.main_left_spin_box)
+        self.area_position_page.registerField('main_position_right', self.main_right_spin_box)
+        self.area_position_page.registerField('main_position_top', self.main_top_spin_box)
+        self.area_position_page.registerField('main_position_bottom', self.main_bottom_spin_box)
+        self.area_position_page.registerField('footer_position_override', self.footer_position_combo_box)
         self.area_position_page.registerField('footer_position_x', self.footer_x_spin_box)
         self.area_position_page.registerField('footer_position_y', self.footer_y_spin_box)
         self.area_position_page.registerField('footer_position_width', self.footer_width_spin_box)
+        self.area_position_page.registerField('footer_position_left', self.footer_left_spin_box)
+        self.area_position_page.registerField('footer_position_right', self.footer_right_spin_box)
+        self.area_position_page.registerField('footer_position_bottom', self.footer_bottom_spin_box)
         self.area_position_page.registerField('footer_position_height', self.footer_height_spin_box)
         self.background_page.registerField('horizontal', self.horizontal_combo_box)
         self.background_page.registerField('vertical', self.vertical_combo_box)
@@ -238,21 +249,31 @@
             self.shadow_size_spin_box.setEnabled(self.theme.font_main_shadow)
             self.calculate_lines()
 
-    def on_main_position_check_box_state_changed(self, value):
+    def on_main_position_combo_box_current_index_changed(self, index):
         """
-        Change state as Main Area _position check box changed
-        NOTE the font_main_override is the inverse of the check box value
+        Change state as Main Area _position combo box changed
+        NOTE the font_main_override
+            0 means to use location defaults
+            1 means to use location user-defined values
+            2 means to use margin defaults
+            3 means to use margin user-defined values
         """
         if self.update_theme_allowed:
-            self.theme.font_main_override = not (value == QtCore.Qt.Checked)
+            self.theme.font_main_override = int(index)
+            self.set_position_page_values()
 
-    def on_footer_position_check_box_state_changed(self, value):
+    def on_footer_position_combo_box_current_index_changed(self, index):
         """
-        Change state as Footer Area _position check box changed
-        NOTE the font_footer_override is the inverse of the check box value
+        Change state as Footer Area _position combo box changed
+        NOTE the font_footer_override
+            0 means to use location defaults
+            1 means to use location user-defined values
+            2 means to use margin defaults
+            3 means to use margin user-defined values
         """
         if self.update_theme_allowed:
-            self.theme.font_footer_override = not (value == QtCore.Qt.Checked)
+            self.theme.font_footer_override = int(index)
+            self.set_position_page_values()
 
     def exec_(self, edit=False):
         """
@@ -348,17 +369,31 @@
         Handle the display and state of the _position page.
         """
         # Main Area
-        self.main_position_check_box.setChecked(not self.theme.font_main_override)
         self.setField('main_position_x', self.theme.font_main_x)
         self.setField('main_position_y', self.theme.font_main_y)
         self.setField('main_position_height', self.theme.font_main_height)
         self.setField('main_position_width', self.theme.font_main_width)
-        # Footer
-        self.footer_position_check_box.setChecked(not self.theme.font_footer_override)
+        self.setField('main_position_left', self.theme.font_main_left)
+        self.setField('main_position_right', self.theme.font_main_right)
+        self.setField('main_position_top', self.theme.font_main_top)
+        self.setField('main_position_bottom', self.theme.font_main_bottom)
+        self.setField('main_position_override', self.theme.font_main_override)
+        self.main_margin_widget.setEnabled(self.theme.font_main_override == 3)
+        self.main_location_widget.setEnabled(self.theme.font_main_override == 1)
+        self.main_position_stack.setCurrentIndex(int(self.theme.font_main_override > 1))
+        # Footer Area
         self.setField('footer_position_x', self.theme.font_footer_x)
         self.setField('footer_position_y', self.theme.font_footer_y)
         self.setField('footer_position_height', self.theme.font_footer_height)
         self.setField('footer_position_width', self.theme.font_footer_width)
+        self.setField('footer_position_left', self.theme.font_footer_left)
+        self.setField('footer_position_right', self.theme.font_footer_right)
+        self.setField('footer_position_bottom', self.theme.font_footer_bottom)
+        self.setField('footer_position_override', self.theme.font_footer_override)
+        self.footer_height_widget.setEnabled(self.theme.font_footer_override % 2)
+        self.footer_margin_widget.setEnabled(self.theme.font_footer_override == 3)
+        self.footer_location_widget.setEnabled(self.theme.font_footer_override == 1)
+        self.footer_position_stack.setCurrentIndex(int(self.theme.font_footer_override > 1))
 
     def set_alignment_page_values(self):
         """
@@ -497,11 +532,18 @@
         self.theme.font_main_y = self.field('main_position_y')
         self.theme.font_main_height = self.field('main_position_height')
         self.theme.font_main_width = self.field('main_position_width')
+        self.theme.font_main_left = self.field('main_position_left')
+        self.theme.font_main_right = self.field('main_position_right')
+        self.theme.font_main_top = self.field('main_position_top')
+        self.theme.font_main_bottom = self.field('main_position_bottom')
         self.theme.font_footer_x = self.field('footer_position_x')
         self.theme.font_footer_y = self.field('footer_position_y')
+        self.theme.font_footer_width = self.field('footer_position_width')
+        self.theme.font_footer_left = self.field('footer_position_left')
+        self.theme.font_footer_right = self.field('footer_position_right')
+        self.theme.font_footer_bottom = self.field('footer_position_bottom')
         self.theme.font_footer_height = self.field('footer_position_height')
-        self.theme.font_footer_width = self.field('footer_position_width')
-        # position page
+        # display page
         self.theme.display_horizontal_align = self.horizontal_combo_box.currentIndex()
         self.theme.display_vertical_align = self.vertical_combo_box.currentIndex()
         self.theme.display_slide_transition = self.field('slide_transition')

=== modified file 'openlp/core/ui/themewizard.py'
--- openlp/core/ui/themewizard.py	2014-10-08 19:42:30 +0000
+++ openlp/core/ui/themewizard.py	2014-10-28 03:51:18 +0000
@@ -274,69 +274,164 @@
         self.area_position_page.setObjectName('area_position_page')
         self.area_position_layout = QtGui.QHBoxLayout(self.area_position_page)
         self.area_position_layout.setObjectName('area_position_layout')
+        # main
         self.main_position_group_box = QtGui.QGroupBox(self.area_position_page)
         self.main_position_group_box.setObjectName('main_position_group_box')
         self.main_position_layout = QtGui.QFormLayout(self.main_position_group_box)
         self.main_position_layout.setObjectName('main_position_layout')
-        self.main_position_check_box = QtGui.QCheckBox(self.main_position_group_box)
-        self.main_position_check_box.setObjectName('main_position_check_box')
-        self.main_position_layout.addRow(self.main_position_check_box)
-        self.main_x_label = QtGui.QLabel(self.main_position_group_box)
+        self.main_position_combo_box = QtGui.QComboBox(self.main_position_group_box)
+        self.main_position_combo_box.addItems(['', '', '', ''])
+        self.main_position_combo_box.setObjectName('main_position_combo_box')
+        self.main_position_layout.addRow(self.main_position_combo_box)
+        self.main_position_stack = QtGui.QStackedLayout()
+        self.main_position_stack.setObjectName('main_position_stack')
+        # main location
+        self.main_location_widget = QtGui.QWidget(self.main_position_group_box)
+        self.main_location_widget.setObjectName('main_location_widget')
+        self.main_location_layout = QtGui.QFormLayout(self.main_location_widget)
+        self.main_location_layout.setObjectName('main_location_layout')
+        self.main_x_label = QtGui.QLabel()
         self.main_x_label.setObjectName('main_x_label')
-        self.main_x_spin_box = QtGui.QSpinBox(self.main_position_group_box)
+        self.main_x_spin_box = QtGui.QSpinBox(self.main_location_widget)
         self.main_x_spin_box.setMaximum(9999)
         self.main_x_spin_box.setObjectName('main_x_spin_box')
-        self.main_position_layout.addRow(self.main_x_label, self.main_x_spin_box)
-        self.main_y_label = QtGui.QLabel(self.main_position_group_box)
+        self.main_location_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
+        self.main_location_layout.addRow(self.main_x_label, self.main_x_spin_box)
+        self.main_y_label = QtGui.QLabel(self.main_location_widget)
         self.main_y_label.setObjectName('main_y_label')
-        self.main_y_spin_box = QtGui.QSpinBox(self.main_position_group_box)
+        self.main_y_spin_box = QtGui.QSpinBox(self.main_location_widget)
         self.main_y_spin_box.setMaximum(9999)
         self.main_y_spin_box.setObjectName('main_y_spin_box')
-        self.main_position_layout.addRow(self.main_y_label, self.main_y_spin_box)
-        self.main_width_label = QtGui.QLabel(self.main_position_group_box)
+        self.main_location_layout.addRow(self.main_y_label, self.main_y_spin_box)
+        self.main_width_label = QtGui.QLabel(self.main_location_widget)
         self.main_width_label.setObjectName('main_width_label')
-        self.main_width_spin_box = QtGui.QSpinBox(self.main_position_group_box)
+        self.main_width_spin_box = QtGui.QSpinBox(self.main_location_widget)
         self.main_width_spin_box.setMaximum(9999)
         self.main_width_spin_box.setObjectName('main_width_spin_box')
-        self.main_position_layout.addRow(self.main_width_label, self.main_width_spin_box)
-        self.main_height_label = QtGui.QLabel(self.main_position_group_box)
+        self.main_location_layout.addRow(self.main_width_label, self.main_width_spin_box)
+        self.main_height_label = QtGui.QLabel(self.main_location_widget)
         self.main_height_label.setObjectName('main_height_label')
-        self.main_height_spin_box = QtGui.QSpinBox(self.main_position_group_box)
+        self.main_height_spin_box = QtGui.QSpinBox(self.main_location_widget)
         self.main_height_spin_box.setMaximum(9999)
         self.main_height_spin_box.setObjectName('main_height_spin_box')
-        self.main_position_layout.addRow(self.main_height_label, self.main_height_spin_box)
+        self.main_location_layout.addRow(self.main_height_label, self.main_height_spin_box)
+        # main margin
+        self.main_margin_widget = QtGui.QWidget(self.main_position_group_box)
+        self.main_margin_widget.setObjectName('main_margin_widget')
+        self.main_margin_layout = QtGui.QFormLayout(self.main_margin_widget)
+        self.main_margin_layout.setObjectName('main_margin_layout')
+        self.main_left_label = QtGui.QLabel()
+        self.main_left_label.setObjectName('main_left_label')
+        self.main_left_spin_box = QtGui.QSpinBox(self.main_margin_widget)
+        self.main_left_spin_box.setMaximum(9999)
+        self.main_left_spin_box.setObjectName('main_left_spin_box')
+        self.main_margin_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
+        self.main_margin_layout.addRow(self.main_left_label, self.main_left_spin_box)
+        self.main_right_label = QtGui.QLabel(self.main_margin_widget)
+        self.main_right_label.setObjectName('main_right_label')
+        self.main_right_spin_box = QtGui.QSpinBox(self.main_margin_widget)
+        self.main_right_spin_box.setMaximum(9999)
+        self.main_right_spin_box.setObjectName('main_right_spin_box')
+        self.main_margin_layout.addRow(self.main_right_label, self.main_right_spin_box)
+        self.main_top_label = QtGui.QLabel(self.main_margin_widget)
+        self.main_top_label.setObjectName('main_top_label')
+        self.main_top_spin_box = QtGui.QSpinBox(self.main_margin_widget)
+        self.main_top_spin_box.setMaximum(9999)
+        self.main_top_spin_box.setObjectName('main_top_spin_box')
+        self.main_margin_layout.addRow(self.main_top_label, self.main_top_spin_box)
+        self.main_bottom_label = QtGui.QLabel(self.main_margin_widget)
+        self.main_bottom_label.setObjectName('main_bottom_label')
+        self.main_bottom_spin_box = QtGui.QSpinBox(self.main_margin_widget)
+        self.main_bottom_spin_box.setMaximum(9999)
+        self.main_bottom_spin_box.setObjectName('main_bottom_spin_box')
+        self.main_margin_layout.addRow(self.main_bottom_label, self.main_bottom_spin_box)
+        self.main_position_stack.addWidget(self.main_location_widget)
+        self.main_position_stack.addWidget(self.main_margin_widget)
+        self.main_position_layout.addRow(self.main_position_stack)
         self.area_position_layout.addWidget(self.main_position_group_box)
+        # footer
         self.footer_position_group_box = QtGui.QGroupBox(self.area_position_page)
         self.footer_position_group_box.setObjectName('footer_position_group_box')
         self.footer_position_layout = QtGui.QFormLayout(self.footer_position_group_box)
+        self.footer_position_layout.setVerticalSpacing(0)
+        self.footer_position_layout.setContentsMargins(0,4,0,0)
         self.footer_position_layout.setObjectName('footer_position_layout')
-        self.footer_position_check_box = QtGui.QCheckBox(self.footer_position_group_box)
-        self.footer_position_check_box.setObjectName('footer_position_check_box')
-        self.footer_position_layout.addRow(self.footer_position_check_box)
-        self.footer_x_label = QtGui.QLabel(self.footer_position_group_box)
+        self.footer_position_combo_box = QtGui.QComboBox(self.footer_position_group_box)
+        self.footer_position_combo_box.addItems(['', '', '', ''])
+        self.footer_position_combo_box.setObjectName('footer_position_combo_box')
+        self.footer_position_layout.addRow(self.footer_position_combo_box)
+        self.footer_position_stack = QtGui.QStackedLayout()
+        self.footer_position_stack.setObjectName('footer_position_stack')
+        # footer location
+        self.footer_location_widget = QtGui.QWidget(self.footer_position_group_box)
+        self.footer_location_widget.setObjectName('footer_location_widget')
+        self.footer_location_layout = QtGui.QFormLayout(self.footer_location_widget)
+        self.footer_location_layout.setVerticalSpacing(4)
+        self.footer_location_layout.setContentsMargins(4,8,0,4)
+        self.footer_location_layout.setObjectName('footer_location_layout')
+        self.footer_x_label = QtGui.QLabel(self.footer_location_widget)
         self.footer_x_label.setObjectName('footer_x_label')
-        self.footer_x_spin_box = QtGui.QSpinBox(self.footer_position_group_box)
+        self.footer_x_spin_box = QtGui.QSpinBox(self.footer_location_widget)
         self.footer_x_spin_box.setMaximum(9999)
         self.footer_x_spin_box.setObjectName('footer_x_spin_box')
-        self.footer_position_layout.addRow(self.footer_x_label, self.footer_x_spin_box)
-        self.footer_y_label = QtGui.QLabel(self.footer_position_group_box)
+        self.footer_location_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
+        self.footer_location_layout.addRow(self.footer_x_label, self.footer_x_spin_box)
+        self.footer_y_label = QtGui.QLabel(self.footer_location_widget)
         self.footer_y_label.setObjectName('footer_y_label')
-        self.footer_y_spin_box = QtGui.QSpinBox(self.footer_position_group_box)
+        self.footer_y_spin_box = QtGui.QSpinBox(self.footer_location_widget)
         self.footer_y_spin_box.setMaximum(9999)
         self.footer_y_spin_box.setObjectName('footer_y_spin_box')
-        self.footer_position_layout.addRow(self.footer_y_label, self.footer_y_spin_box)
-        self.footer_width_label = QtGui.QLabel(self.footer_position_group_box)
+        self.footer_location_layout.addRow(self.footer_y_label, self.footer_y_spin_box)
+        self.footer_width_label = QtGui.QLabel(self.footer_location_widget)
         self.footer_width_label.setObjectName('footer_width_label')
-        self.footer_width_spin_box = QtGui.QSpinBox(self.footer_position_group_box)
+        self.footer_width_spin_box = QtGui.QSpinBox(self.footer_location_widget)
         self.footer_width_spin_box.setMaximum(9999)
         self.footer_width_spin_box.setObjectName('footer_width_spin_box')
-        self.footer_position_layout.addRow(self.footer_width_label, self.footer_width_spin_box)
-        self.footer_height_label = QtGui.QLabel(self.footer_position_group_box)
+        self.footer_location_layout.addRow(self.footer_width_label, self.footer_width_spin_box)
+        self.footer_position_stack.addWidget(self.footer_location_widget)
+        # footer margin
+        self.footer_margin_widget = QtGui.QWidget(self.footer_position_group_box)
+        self.footer_margin_widget.setObjectName('footer_margin_widget')
+        self.footer_margin_layout = QtGui.QFormLayout(self.footer_margin_widget)
+        self.footer_margin_layout.setVerticalSpacing(4)
+        self.footer_margin_layout.setContentsMargins(4,8,0,4)
+        self.footer_margin_layout.setObjectName('footer_margin_layout')
+        self.footer_left_label = QtGui.QLabel(self.footer_margin_widget)
+        self.footer_left_label.setObjectName('footer_left_label')
+        self.footer_left_spin_box = QtGui.QSpinBox(self.footer_margin_widget)
+        self.footer_left_spin_box.setMaximum(9999)
+        self.footer_left_spin_box.setObjectName('footer_left_spin_box')
+        self.footer_margin_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
+        self.footer_margin_layout.addRow(self.footer_left_label, self.footer_left_spin_box)
+        self.footer_right_label = QtGui.QLabel(self.footer_margin_widget)
+        self.footer_right_label.setObjectName('footer_right_label')
+        self.footer_right_spin_box = QtGui.QSpinBox(self.footer_margin_widget)
+        self.footer_right_spin_box.setMaximum(9999)
+        self.footer_right_spin_box.setObjectName('footer_right_spin_box')
+        self.footer_margin_layout.addRow(self.footer_right_label, self.footer_right_spin_box)
+        self.footer_bottom_label = QtGui.QLabel(self.footer_margin_widget)
+        self.footer_bottom_label.setObjectName('footer_bottom_label')
+        self.footer_bottom_spin_box = QtGui.QSpinBox(self.footer_margin_widget)
+        self.footer_bottom_spin_box.setMaximum(9999)
+        self.footer_bottom_spin_box.setObjectName('footer_bottom_spin_box')
+        self.footer_margin_layout.addRow(self.footer_bottom_label, self.footer_bottom_spin_box)
+        self.footer_position_stack.addWidget(self.footer_margin_widget)
+        self.footer_position_layout.addRow(self.footer_position_stack)
+        # footer height
+        self.footer_height_widget = QtGui.QWidget(self.footer_position_group_box)
+        self.footer_height_widget.setObjectName('footer_height_widget')
+        self.footer_height_layout = QtGui.QFormLayout(self.footer_height_widget)
+        self.footer_height_layout.setVerticalSpacing(0)
+        self.footer_height_layout.setContentsMargins(0,0,0,0)
+        self.footer_height_layout.setObjectName('footer_height_layout')
+        self.footer_height_label = QtGui.QLabel(self.footer_height_widget)
         self.footer_height_label.setObjectName('footer_height_label')
-        self.footer_height_spin_box = QtGui.QSpinBox(self.footer_position_group_box)
+        self.footer_height_spin_box = QtGui.QSpinBox(self.footer_height_widget)
         self.footer_height_spin_box.setMaximum(9999)
         self.footer_height_spin_box.setObjectName('footer_height_spin_box')
-        self.footer_position_layout.addRow(self.footer_height_label, self.footer_height_spin_box)
+        self.footer_height_layout.setItem(1, QtGui.QFormLayout.LabelRole, self.spacer)
+        self.footer_height_layout.addRow(self.footer_height_label, self.footer_height_spin_box)
+        self.footer_position_layout.addWidget(self.footer_height_widget)
         self.area_position_layout.addWidget(self.footer_position_group_box)
         theme_wizard.addPage(self.area_position_page)
         # Preview Page
@@ -378,22 +473,6 @@
                                QtCore.SLOT('setEnabled(bool)'))
         QtCore.QObject.connect(self.shadow_check_box, QtCore.SIGNAL('toggled(bool)'), self.shadow_size_spin_box,
                                QtCore.SLOT('setEnabled(bool)'))
-        QtCore.QObject.connect(self.main_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.main_x_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.main_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.main_y_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.main_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.main_width_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.main_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.main_height_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.footer_x_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'), self.footer_y_spin_box,
-                               QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'),
-                               self.footer_width_spin_box, QtCore.SLOT('setDisabled(bool)'))
-        QtCore.QObject.connect(self.footer_position_check_box, QtCore.SIGNAL('toggled(bool)'),
-                               self.footer_height_spin_box, QtCore.SLOT('setDisabled(bool)'))
 
     def retranslateUi(self, theme_wizard):
         """
@@ -469,7 +548,14 @@
         self.area_position_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Allows you to change and move the'
                                                       ' Main and Footer areas.'))
         self.main_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Main Area'))
-        self.main_position_check_box.setText(translate('OpenLP.ThemeWizard', '&Use default location'))
+        self.main_position_combo_box.setItemText(0,
+                                      translate('OpenLP.ThemeWizard', 'Use default location'))
+        self.main_position_combo_box.setItemText(1,
+                                      translate('OpenLP.ThemeWizard', 'Specify custom location'))
+        self.main_position_combo_box.setItemText(2,
+                                      translate('OpenLP.ThemeWizard', 'Use default margins'))
+        self.main_position_combo_box.setItemText(3,
+                                      translate('OpenLP.ThemeWizard', 'Specify custom margins'))
         self.main_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
         self.main_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         self.main_y_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
@@ -478,7 +564,23 @@
         self.main_width_label.setText(translate('OpenLP.ThemeWizard', 'Width:'))
         self.main_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         self.main_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
+        self.main_left_label.setText(translate('OpenLP.ThemeWizard', 'Left:'))
+        self.main_left_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+        self.main_right_label.setText(translate('OpenLP.ThemeWizard', 'Right:'))
+        self.main_right_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+        self.main_top_label.setText(translate('OpenLP.ThemeWizard', 'Top:'))
+        self.main_top_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+        self.main_bottom_label.setText(translate('OpenLP.ThemeWizard', 'Bottom:'))
+        self.main_bottom_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         self.footer_position_group_box.setTitle(translate('OpenLP.ThemeWizard', '&Footer Area'))
+        self.footer_position_combo_box.setItemText(0,
+                                      translate('OpenLP.ThemeWizard', 'Use default location'))
+        self.footer_position_combo_box.setItemText(1,
+                                      translate('OpenLP.ThemeWizard', 'Specify custom location'))
+        self.footer_position_combo_box.setItemText(2,
+                                      translate('OpenLP.ThemeWizard', 'Use default margins'))
+        self.footer_position_combo_box.setItemText(3,
+                                      translate('OpenLP.ThemeWizard', 'Specify custom margins'))
         self.footer_x_label.setText(translate('OpenLP.ThemeWizard', 'X position:'))
         self.footer_x_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         self.footer_y_label.setText(translate('OpenLP.ThemeWizard', 'Y position:'))
@@ -487,7 +589,12 @@
         self.footer_width_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         self.footer_height_label.setText(translate('OpenLP.ThemeWizard', 'Height:'))
         self.footer_height_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
-        self.footer_position_check_box.setText(translate('OpenLP.ThemeWizard', 'Use default location'))
+        self.footer_left_label.setText(translate('OpenLP.ThemeWizard', 'Left:'))
+        self.footer_left_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+        self.footer_right_label.setText(translate('OpenLP.ThemeWizard', 'Right:'))
+        self.footer_right_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
+        self.footer_bottom_label.setText(translate('OpenLP.ThemeWizard', 'Bottom:'))
+        self.footer_bottom_spin_box.setSuffix(translate('OpenLP.ThemeWizard', 'px'))
         theme_wizard.setOption(QtGui.QWizard.HaveCustomButton1, False)
         theme_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.ThemeWizard', 'Layout Preview'))
         self.preview_page.setTitle(translate('OpenLP.ThemeWizard', 'Preview and Save'))

=== modified file 'tests/functional/openlp_core_lib/test_theme.py'
--- tests/functional/openlp_core_lib/test_theme.py	2014-09-07 22:17:20 +0000
+++ tests/functional/openlp_core_lib/test_theme.py	2014-10-28 03:51:18 +0000
@@ -68,4 +68,4 @@
         self.assertTrue(default_theme.font_footer_name == "Arial",
                         'The theme should have a font_footer_name of Arial')
         self.assertTrue(default_theme.font_main_bold is False, 'The theme should have a font_main_bold of false')
-        self.assertTrue(len(default_theme.__dict__) == 47, 'The theme should have 47 variables')
+        self.assertTrue(len(default_theme.__dict__) == 57, 'The theme should have 57 variables')
\ No newline at end of file


Follow ups