final UserPreferencesController controller) {
    if (controller.isPropertyEditable(UserPreferencesController.Property.LANGUAGE)) {
      // Create language label and combo box bound to controller LANGUAGE property
      this.languageLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "languageLabel.text"));    
      this.languageComboBox = new JComboBox(new DefaultComboBoxModel(preferences.getSupportedLanguages()));
      this.languageComboBox.setRenderer(new DefaultListCellRenderer() {
          @Override
          public Component getListCellRendererComponent(JList list, 
              Object value, int index, boolean isSelected, boolean cellHasFocus) {
            String language = (String)value;
            Locale locale;
            int underscoreIndex = language.indexOf("_");
            if (underscoreIndex != -1) {
              locale = new Locale(language.substring(0, underscoreIndex), 
                  language.substring(underscoreIndex + 1));
            } else {
              locale = new Locale(language);
            }
            String displayedValue = locale.getDisplayLanguage(locale);
            displayedValue = Character.toUpperCase(displayedValue.charAt(0)) + displayedValue.substring(1);
            if (underscoreIndex != -1) {
              displayedValue += " - " + locale.getDisplayCountry(locale); 
            }
            return super.getListCellRendererComponent(list, displayedValue, index, isSelected,
                cellHasFocus);
          }
        });
      this.languageComboBox.setMaximumRowCount(this.languageComboBox.getModel().getSize());
      this.languageComboBox.setSelectedItem(controller.getLanguage());
      this.languageComboBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setLanguage((String)languageComboBox.getSelectedItem());
          }
        });
      controller.addPropertyChangeListener(UserPreferencesController.Property.LANGUAGE, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              languageComboBox.setSelectedItem(controller.getLanguage());
            }
          });
    }
    
    if (controller.isPropertyEditable(UserPreferencesController.Property.UNIT)) {
      // Create unit label and radio buttons bound to controller UNIT property
      this.unitLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "unitLabel.text"));
      this.centimeterRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "centimeterRadioButton.text"), 
          controller.getUnit() == LengthUnit.CENTIMETER);
      this.centimeterRadioButton.setActionCommand(LengthUnit.CENTIMETER.name());
      this.inchRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "inchRadioButton.text"), 
          controller.getUnit() == LengthUnit.INCH);
      this.inchRadioButton.setActionCommand(LengthUnit.INCH.name());
      this.millimeterRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "millimeterRadioButton.text"), 
          controller.getUnit() == LengthUnit.MILLIMETER);
      this.millimeterRadioButton.setActionCommand(LengthUnit.MILLIMETER.name());
      this.meterRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "meterRadioButton.text"), 
          controller.getUnit() == LengthUnit.METER);
      this.meterRadioButton.setActionCommand(LengthUnit.METER.name());
      final ButtonGroup unitButtonGroup = new ButtonGroup();
      unitButtonGroup.add(this.centimeterRadioButton);
      unitButtonGroup.add(this.inchRadioButton);
      unitButtonGroup.add(this.millimeterRadioButton);
      unitButtonGroup.add(this.meterRadioButton);
      ItemListener unitChangeListener = new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setUnit(LengthUnit.valueOf(unitButtonGroup.getSelection().getActionCommand())); 
          }
        };
      this.centimeterRadioButton.addItemListener(unitChangeListener);
      this.inchRadioButton.addItemListener(unitChangeListener);
      this.millimeterRadioButton.addItemListener(unitChangeListener);
      this.meterRadioButton.addItemListener(unitChangeListener);
      controller.addPropertyChangeListener(UserPreferencesController.Property.UNIT, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              switch (controller.getUnit()) {
                case CENTIMETER :
                  centimeterRadioButton.setSelected(true);
                  break;
                case INCH :
                  inchRadioButton.setSelected(true);
                  break;
                case MILLIMETER :
                  millimeterRadioButton.setSelected(true);
                  break;
                case METER :
                  meterRadioButton.setSelected(true);
                  break;
              }
            }
          });
    }
    
    if (controller.isPropertyEditable(UserPreferencesController.Property.FURNITURE_CATALOG_VIEWED_IN_TREE)) {
      // Create furniture catalog label and radio buttons bound to controller FURNITURE_CATALOG_VIEWED_IN_TREE property
      this.furnitureCatalogViewLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "furnitureCatalogViewLabel.text"));
      this.treeRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "treeRadioButton.text"), 
          controller.isFurnitureCatalogViewedInTree());
      this.listRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "listRadioButton.text"), 
          !controller.isFurnitureCatalogViewedInTree());
      ButtonGroup furnitureCatalogViewButtonGroup = new ButtonGroup();
      furnitureCatalogViewButtonGroup.add(this.treeRadioButton);
      furnitureCatalogViewButtonGroup.add(this.listRadioButton);
  
      ItemListener furnitureCatalogViewChangeListener = new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setFurnitureCatalogViewedInTree(treeRadioButton.isSelected());
          }
        };
      this.treeRadioButton.addItemListener(furnitureCatalogViewChangeListener);
      this.listRadioButton.addItemListener(furnitureCatalogViewChangeListener);
      controller.addPropertyChangeListener(UserPreferencesController.Property.FURNITURE_CATALOG_VIEWED_IN_TREE, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              treeRadioButton.setSelected(controller.isFurnitureCatalogViewedInTree());
            }
          });
    }
    if (controller.isPropertyEditable(UserPreferencesController.Property.NAVIGATION_PANEL_VISIBLE)
        && !"true".equalsIgnoreCase(System.getProperty("com.eteks.sweethome3d.no3D"))) {
      // Create navigation panel label and check box bound to controller NAVIGATION_PANEL_VISIBLE property
      this.navigationPanelLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "navigationPanelLabel.text"));
      this.navigationPanelCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "navigationPanelCheckBox.text"));
      if (!OperatingSystem.isMacOSX()
          || OperatingSystem.isMacOSXLeopardOrSuperior()) {
        this.navigationPanelCheckBox.setSelected(controller.isNavigationPanelVisible());
        this.navigationPanelCheckBox.addItemListener(new ItemListener() {
            public void itemStateChanged(ItemEvent ev) {
              controller.setNavigationPanelVisible(navigationPanelCheckBox.isSelected());
            }
          });
        controller.addPropertyChangeListener(UserPreferencesController.Property.NAVIGATION_PANEL_VISIBLE, 
            new PropertyChangeListener() {
              public void propertyChange(PropertyChangeEvent ev) {
                navigationPanelCheckBox.setSelected(controller.isNavigationPanelVisible());
              }
            });
      } else {
        // No support for navigation panel under Mac OS X Tiger (too unstable)
        this.navigationPanelCheckBox.setEnabled(false);
      }
    }
    if (controller.isPropertyEditable(UserPreferencesController.Property.MAGNETISM_ENABLED)) {
      // Create magnetism label and check box bound to controller MAGNETISM_ENABLED property
      this.magnetismLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "magnetismLabel.text"));
      this.magnetismCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "magnetismCheckBox.text"), controller.isMagnetismEnabled());
      this.magnetismCheckBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setMagnetismEnabled(magnetismCheckBox.isSelected());
          }
        });
      controller.addPropertyChangeListener(UserPreferencesController.Property.MAGNETISM_ENABLED, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              magnetismCheckBox.setSelected(controller.isMagnetismEnabled());
            }
          });
    }
    if (controller.isPropertyEditable(UserPreferencesController.Property.RULERS_VISIBLE)) {
      // Create rulers label and check box bound to controller RULERS_VISIBLE property
      this.rulersLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "rulersLabel.text"));
      this.rulersCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "rulersCheckBox.text"), controller.isRulersVisible());
      this.rulersCheckBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setRulersVisible(rulersCheckBox.isSelected());
          }
        });
      controller.addPropertyChangeListener(UserPreferencesController.Property.RULERS_VISIBLE, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              rulersCheckBox.setSelected(controller.isRulersVisible());
            }
          });
    }
    
    if (controller.isPropertyEditable(UserPreferencesController.Property.GRID_VISIBLE)) {
      // Create grid label and check box bound to controller GRID_VISIBLE property
      this.gridLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "gridLabel.text"));
      this.gridCheckBox = new JCheckBox(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "gridCheckBox.text"), controller.isGridVisible());
      this.gridCheckBox.addItemListener(new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setGridVisible(gridCheckBox.isSelected());
          }
        });
      controller.addPropertyChangeListener(UserPreferencesController.Property.GRID_VISIBLE, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              gridCheckBox.setSelected(controller.isGridVisible());
            }
          });
    }
    
    if (controller.isPropertyEditable(UserPreferencesController.Property.FURNITURE_VIEWED_FROM_TOP)) {
      // Create furniture appearance label and radio buttons bound to controller FURNITURE_VIEWED_FROM_TOP property
      this.furnitureIconLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "furnitureIconLabel.text"));
      this.catalogIconRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "catalogIconRadioButton.text"), 
          !controller.isFurnitureViewedFromTop());
      this.topViewRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "topViewRadioButton.text"), 
          controller.isFurnitureViewedFromTop());
      if ("true".equalsIgnoreCase(System.getProperty("com.eteks.sweethome3d.no3D"))) {
        this.catalogIconRadioButton.setEnabled(false);
        this.topViewRadioButton.setEnabled(false);
      } else { 
        if (Component3DManager.getInstance().isOffScreenImageSupported()) {
          ButtonGroup furnitureAppearanceButtonGroup = new ButtonGroup();
          furnitureAppearanceButtonGroup.add(this.catalogIconRadioButton);
          furnitureAppearanceButtonGroup.add(this.topViewRadioButton);
      
          ItemListener furnitureAppearanceChangeListener = new ItemListener() {
              public void itemStateChanged(ItemEvent ev) {
                controller.setFurnitureViewedFromTop(topViewRadioButton.isSelected());
              }
            };
          this.catalogIconRadioButton.addItemListener(furnitureAppearanceChangeListener);
          this.topViewRadioButton.addItemListener(furnitureAppearanceChangeListener);
          controller.addPropertyChangeListener(UserPreferencesController.Property.FURNITURE_VIEWED_FROM_TOP, 
              new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent ev) {
                  topViewRadioButton.setSelected(controller.isFurnitureViewedFromTop());
                }
              });
        } else {
          this.catalogIconRadioButton.setEnabled(false);
          this.topViewRadioButton.setEnabled(false);
        }
      }
    }
    if (controller.isPropertyEditable(UserPreferencesController.Property.ROOM_FLOOR_COLORED_OR_TEXTURED)) {
      // Create room rendering label and radio buttons bound to controller ROOM_FLOOR_COLORED_OR_TEXTURED property
      this.roomRenderingLabel = new JLabel(preferences.getLocalizedString(
          UserPreferencesPanel.class, "roomRenderingLabel.text"));
      this.monochromeRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "monochromeRadioButton.text"), 
          !controller.isRoomFloorColoredOrTextured());
      this.floorColorOrTextureRadioButton = new JRadioButton(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "floorColorOrTextureRadioButton.text"), 
          controller.isRoomFloorColoredOrTextured());
      ButtonGroup roomRenderingButtonGroup = new ButtonGroup();
      roomRenderingButtonGroup.add(this.monochromeRadioButton);
      roomRenderingButtonGroup.add(this.floorColorOrTextureRadioButton);
      ItemListener roomRenderingChangeListener = new ItemListener() {
          public void itemStateChanged(ItemEvent ev) {
            controller.setRoomFloorColoredOrTextured(floorColorOrTextureRadioButton.isSelected());
          }
        };
      this.monochromeRadioButton.addItemListener(roomRenderingChangeListener);
      this.floorColorOrTextureRadioButton.addItemListener(roomRenderingChangeListener);
      controller.addPropertyChangeListener(UserPreferencesController.Property.ROOM_FLOOR_COLORED_OR_TEXTURED, 
          new PropertyChangeListener() {
            public void propertyChange(PropertyChangeEvent ev) {
              floorColorOrTextureRadioButton.setSelected(controller.isRoomFloorColoredOrTextured());
            }
          });
    }
    if (controller.isPropertyEditable(UserPreferencesController.Property.WALL_PATTERN)) {
      // Create wall pattern label and combo box bound to controller WALL_PATTERN property
      this.wallPatternLabel = new JLabel(SwingTools.getLocalizedLabelText(preferences, 
          UserPreferencesPanel.class, "wallPatternLabel.text"));    
      List<TextureImage> patterns = preferences.getPatternsCatalog().getPatterns();
      this.wallPatternComboBox = new JComboBox(new DefaultComboBoxModel(patterns.toArray()));
      this.wallPatternComboBox.setRenderer(new DefaultListCellRenderer() {
          @Override
          public Component getListCellRendererComponent(final JList list, 
              Object value, int index, boolean isSelected, boolean cellHasFocus) {
            TextureImage wallPattern = (TextureImage)value;