/*   **********************************************************************  **
 **   Copyright notice                                                       **
 **                                                                          **
 **   (c) 2003-2006 RSSOwl Development Team                                  **
 **   http://www.rssowl.org/                                                 **
 **                                                                          **
 **   All rights reserved                                                    **
 **                                                                          **
 **   This program and the accompanying materials are made available under   **
 **   the terms of the Eclipse Public License 1.0 which accompanies this     **
 **   distribution, and is available at:                                     **
 **   http://www.rssowl.org/legal/epl-v10.html                               **
 **                                                                          **
 **   A copy is found in the file epl-v10.html and important notices to the  **
 **   license from the team is found in the textfile LICENSE.txt distributed **
 **   in this package.                                                       **
 **                                                                          **
 **   This copyright notice MUST APPEAR in all copies of the file!           **
 **                                                                          **
 **   Contributors:                                                          **
 **     RSSOwl - initial API and implementation (bpasero@rssowl.org)         **
 **                                                                          **
 **  **********************************************************************  */

package net.sourceforge.rssowl.controller.dialog;

import net.sourceforge.rssowl.controller.DisposeListenerImpl;
import net.sourceforge.rssowl.controller.EventManager;
import net.sourceforge.rssowl.controller.GUI;
import net.sourceforge.rssowl.controller.properties.BloggerProperties;
import net.sourceforge.rssowl.controller.properties.BrowserProperties;
import net.sourceforge.rssowl.controller.properties.ColorProperties;
import net.sourceforge.rssowl.controller.properties.ConnectionProperties;
import net.sourceforge.rssowl.controller.properties.FavoritesProperties;
import net.sourceforge.rssowl.controller.properties.FontProperties;
import net.sourceforge.rssowl.controller.properties.HotkeysProperties;
import net.sourceforge.rssowl.controller.properties.LanguageProperties;
import net.sourceforge.rssowl.controller.properties.MailProperties;
import net.sourceforge.rssowl.controller.properties.PropertiesFilter;
import net.sourceforge.rssowl.controller.properties.PropertyPage;
import net.sourceforge.rssowl.controller.properties.SortOrderPropertiers;
import net.sourceforge.rssowl.controller.properties.SystemTrayProperties;
import net.sourceforge.rssowl.controller.properties.ViewProperties;
import net.sourceforge.rssowl.controller.properties.WorkbenchProperties;
import net.sourceforge.rssowl.dao.SettingsSaver;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.i18n.ITranslatable;
import net.sourceforge.rssowl.util.shop.FontShop;
import net.sourceforge.rssowl.util.shop.LayoutDataShop;
import net.sourceforge.rssowl.util.shop.LayoutShop;
import net.sourceforge.rssowl.util.shop.PaintShop;
import net.sourceforge.rssowl.util.shop.SimpleFileShop;
import net.sourceforge.rssowl.util.shop.StringShop;
import net.sourceforge.rssowl.util.shop.WidgetShop;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.PaintEvent;
import org.eclipse.swt.events.PaintListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.TraverseEvent;
import org.eclipse.swt.events.TraverseListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

/**
 * The preferences dialog allows the user to change RSSOwl's preferences. The
 * user selects one of the categories from the left tree. Every category offers
 * a "Restore defaults" button to reset to the default settings. The "Apply" or
 * "OK" Button saves the made settings.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class PreferencesDialog extends Dialog implements ITranslatable {

  /** Remember the last opened property page */
  public static int lastOpenedPropertyPage = 0;

  /** Min. width of the dialog in DLUs */
  private static final int dialogMinWidth = 460;

  /** Flag to block Filtering of Preferences Pages */
  static boolean blockFilter = false;

  private TreeItem blogger;
  private TreeItem browser;
  private Composite buttonHolder;
  private ToolItem clearFilter;
  private TreeItem colors;
  private TreeItem connection;
  private Composite contentHolder;
  private Button exportSettingsButton;
  private TreeItem favorites;
  private TreeItem font;
  private TreeItem formatNewsTipMail;
  private TreeItem hotkeys;
  private Composite importExportHolder;
  private Button importSettingsButton;
  private Label labelPrefTitle;
  private TreeItem language;
  private String lastSelectedItemText;
  private PropertiesFilter propertiesFilter;
  private TreeItem sortOrder;
  private TreeItem systemTray;
  private String title;
  private TreeItem view;
  PropertyPage activePropertyPage;
  EventManager eventManager;
  Text filterText;
  boolean isFirstFilterInputFocus;
  Label labelImgHolder;
  Composite prefTitleHolder;
  GUI rssOwlGui;
  Tree tree;
  TreeItem workbench;

  /**
   * Creates a Dialog with a tree holding different categories. Each selected
   * tree item will open a view to change some settings. Note that the dialog
   * will have no visual representation (no widgets) until it is told to open.
   * <p>
   * Note that the <code>open</code> method blocks for input dialogs.
   * </p>
   * 
   * @param parentShell the parent shell
   * @param dialogTitle the dialog title, or <code>null</code> if none
   * @param rssOwlGui The MainController
   */
  public PreferencesDialog(Shell parentShell, String dialogTitle, GUI rssOwlGui) {
    super(parentShell);
    this.title = dialogTitle;
    this.rssOwlGui = rssOwlGui;
    this.eventManager = rssOwlGui.getEventManager();
    isFirstFilterInputFocus = true;

    /** Create a new Filter */
    propertiesFilter = new PropertiesFilter();

    /** Init the PropertyChangeManager */
    PropertyPage.initPropertyChangeManager(rssOwlGui);
  }

  /** Update I18N - Is only called from the Language properties view! */
  public void updateI18N() {
    if (WidgetShop.isset(blogger))
      blogger.setText(GUI.i18n.getTranslation("MENU_BLOGGER"));

    if (WidgetShop.isset(colors))
      colors.setText(GUI.i18n.getTranslation("MENU_COLORS"));

    if (WidgetShop.isset(workbench))
      workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));

    if (WidgetShop.isset(view))
      view.setText(GUI.i18n.getTranslation("MENU_WINDOW"));

    if (WidgetShop.isset(browser))
      browser.setText(GUI.i18n.getTranslation("MENU_BROWSER"));

    if (WidgetShop.isset(language))
      language.setText(GUI.i18n.getTranslation("MENU_LANGUAGE"));

    if (WidgetShop.isset(font))
      font.setText(GUI.i18n.getTranslation("MENU_FONT"));

    if (WidgetShop.isset(hotkeys))
      hotkeys.setText(GUI.i18n.getTranslation("MENU_HOTKEYS"));

    if (WidgetShop.isset(favorites))
      favorites.setText(GUI.i18n.getTranslation("HEADER_RSS_FAVORITES"));

    if (WidgetShop.isset(connection))
      connection.setText(GUI.i18n.getTranslation("MENU_CONNECTION"));

    if (WidgetShop.isset(formatNewsTipMail))
      formatNewsTipMail.setText(GUI.i18n.getTranslation("MENU_NEWSTIP_MAIL"));

    if (WidgetShop.isset(sortOrder))
      sortOrder.setText(GUI.i18n.getTranslation("LABEL_SORT_ORDER"));

    if (WidgetShop.isset(systemTray))
      systemTray.setText(GUI.i18n.getTranslation("GROUP_TRAY"));

    getShell().setText(GUI.i18n.getTranslation("MENU_PREFERENCES"));

    /** Create a new Filter */
    propertiesFilter = new PropertiesFilter();

    /** Update the propertyview from which the method was called */
    createLanguageProps();

    /** Update Dialog Buttons */
    clearFilter.setToolTipText(GUI.i18n.getTranslation("BUTTON_CLEAR"));
    getButton(IDialogConstants.OK_ID).setText(GUI.i18n.getTranslation("BUTTON_OK"));
    getButton(IDialogConstants.CANCEL_ID).setText(GUI.i18n.getTranslation("BUTTON_CANCLE"));
    importSettingsButton.setText(GUI.i18n.getTranslation("MENU_IMPORT") + "...");
    exportSettingsButton.setText(GUI.i18n.getTranslation("MENU_EXPORT") + "...");

    /** Update Buttons Layout Data */
    setButtonLayoutData(importSettingsButton);
    setButtonLayoutData(exportSettingsButton);
    setButtonLayoutData(getButton(IDialogConstants.OK_ID));
    setButtonLayoutData(getButton(IDialogConstants.CANCEL_ID));
    importExportHolder.layout();
    buttonHolder.layout();

    /** Update layout */
    contentHolder.layout();
  }

  /** Create new PropertyPage: Blogger */
  private void createBloggerProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_BLOGGER"));
    activePropertyPage = new BloggerProperties(contentHolder);
  }

  /** Create new PropertyPage: Browser */
  private void createBrowserProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_BROWSER"));
    activePropertyPage = new BrowserProperties(contentHolder);
  }

  /** Create new PropertyPage: Colors */
  private void createColorProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_COLORS"));
    activePropertyPage = new ColorProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: Connection */
  private void createConnectionProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_CONNECTION"));
    activePropertyPage = new ConnectionProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: Favorites */
  private void createFavoritesProps() {
    renewPropertyPage(GUI.i18n.getTranslation("HEADER_RSS_FAVORITES"));
    activePropertyPage = new FavoritesProperties(contentHolder);
  }

  /** Create new PropertyPage: Font */
  private void createFontProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_FONT"));
    activePropertyPage = new FontProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: Hotkeys */
  private void createHotkeysProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_HOTKEYS"));
    activePropertyPage = new HotkeysProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: Language */
  private void createLanguageProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_LANGUAGE"));
    activePropertyPage = new LanguageProperties(contentHolder, rssOwlGui, this);
  }

  /** Create new PropertyPage: Mail */
  private void createMailProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_NEWSTIP_MAIL"));
    activePropertyPage = new MailProperties(contentHolder);
  }

  /** Create new PropertyPage: Sort Order */
  private void createSortOrderProps() {
    renewPropertyPage(GUI.i18n.getTranslation("LABEL_SORT_ORDER"));
    activePropertyPage = new SortOrderPropertiers(contentHolder);
  }

  /** Create new PropertyPage: System Tray */
  private void createSystemTrayProps() {
    renewPropertyPage(GUI.i18n.getTranslation("GROUP_TRAY"));
    activePropertyPage = new SystemTrayProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: View */
  private void createViewProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_WINDOW"));
    activePropertyPage = new ViewProperties(contentHolder, rssOwlGui);
  }

  /** Create new PropertyPage: Workbench */
  private void createWorkbenchProps() {
    renewPropertyPage(GUI.i18n.getTranslation("MENU_WORKBENCH"));
    activePropertyPage = new WorkbenchProperties(contentHolder, rssOwlGui);
  }

  /**
   * Renew the property page and set a new title
   * 
   * @param title A new title for the PropertyPage
   */
  private void renewPropertyPage(String title) {

    /** Update PropertyChangeManager and Dispose old PropertiesPage */
    if (activePropertyPage != null) {
      activePropertyPage.updatePropertiesChangeManager();
      activePropertyPage.dispose();
    }

    /** New preferences title */
    if (labelPrefTitle == null) {
      labelPrefTitle = new Label(prefTitleHolder, SWT.LEFT);
      labelPrefTitle.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));
      labelPrefTitle.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
      labelPrefTitle.setFont(FontShop.dialogBoldFont);
    }

    /** Image for the title */
    if (labelImgHolder == null) {
      labelImgHolder = new Label(prefTitleHolder, SWT.NONE);
      labelImgHolder.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));
      labelImgHolder.setImage(PaintShop.iconBlueStripes);
      labelImgHolder.setLayoutData(new GridData(SWT.END, SWT.END, false, false));
    }

    /** Update */
    labelPrefTitle.setText(title);
    labelPrefTitle.update();
    labelImgHolder.update();
    prefTitleHolder.layout();
    contentHolder.layout();
  }

  /**
   * Restore the Selection in the Tree for the given Value.
   * 
   * @param selectionText The Value of the selected Item.
   * @param items The Items to search for the Selected one.
   */
  private void restoreSelection(String selectionText, TreeItem items[]) {

    /** Foreach Item */
    for (int i = 0; i < items.length; i++) {
      TreeItem item = items[i];
      if (selectionText.equals(item.getText())) {
        tree.setSelection(new TreeItem[] { item });
        break;
      }

      /** Proceed search recursivley */
      if (item.getItemCount() != 0)
        restoreSelection(selectionText, item.getItems());
    }
  }

  /**
   * Set the selected tree item
   * 
   * @param propertyPage The propertypage number
   */
  private void setTreeSelection(int propertyPage) {
    switch (propertyPage) {

      /** Workbench */
      case 0:
        tree.setSelection(new TreeItem[] { workbench });
        break;

      /** Colors */
      case 1:
        tree.setSelection(new TreeItem[] { colors });
        break;

      /** Fonts */
      case 2:
        tree.setSelection(new TreeItem[] { font });
        break;

      /** Hotkeys */
      case 3:
        tree.setSelection(new TreeItem[] { hotkeys });
        break;

      /** Language */
      case 4:
        tree.setSelection(new TreeItem[] { language });
        break;

      /** Sort Order */
      case 5:
        tree.setSelection(new TreeItem[] { sortOrder });
        break;

      /** View */
      case 6:
        tree.setSelection(new TreeItem[] { view });
        break;

      /** System Tray */
      case 7:
        tree.setSelection(new TreeItem[] { systemTray });
        break;

      /** Blogger */
      case 8:
        tree.setSelection(new TreeItem[] { blogger });
        break;

      /** Browser */
      case 9:
        tree.setSelection(new TreeItem[] { browser });
        break;

      /** Favorites */
      case 10:
        tree.setSelection(new TreeItem[] { favorites });
        break;

      /** Format NewsTip Mail */
      case 11:
        tree.setSelection(new TreeItem[] { formatNewsTipMail });
        break;

      /** Connection */
      case 12:
        tree.setSelection(new TreeItem[] { connection });
        break;
    }
  }

  /**
   * @see org.eclipse.jface.dialogs.Dialog#buttonPressed(int)
   */
  protected void buttonPressed(int buttonId) {

    /** Apply settings, save settings */
    if (buttonId == IDialogConstants.OK_ID)
      saveSettings();

    /** Cleanup */
    if (activePropertyPage != null)
      activePropertyPage.dispose();

    super.buttonPressed(buttonId);
  }

  /**
   * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell)
   */
  protected void configureShell(Shell shell) {
    shell.setLayout(LayoutShop.createGridLayout(1, 0, 5));

    /** On Mac do not set Shell Image since it will change the Dock Image */
    if (!GlobalSettings.isMac())
      shell.setImages(PaintShop.iconOwl);

    shell.setText(title);
    shell.setSize(0, 0);

    /** Listen for the shell to dispose */
    shell.addDisposeListener(new DisposeListener() {
      public void widgetDisposed(DisposeEvent e) {

        /** Reset a flag */
        FontProperties.setPossibleFontChange(false);
      }
    });
  }

  /**
   * @see org.eclipse.jface.dialogs.Dialog#createButtonBar(org.eclipse.swt.widgets.Composite)
   */
  protected Control createButtonBar(Composite parent) {

    /** Composite to hold the bottom buttons */
    buttonHolder = new Composite(parent, SWT.NONE);
    buttonHolder.setLayout(LayoutShop.createGridLayout(2, 0, 0, 5, 5, false));
    buttonHolder.setLayoutData(LayoutDataShop.createGridData(GridData.FILL_HORIZONTAL, 2));

    /** Composite to store Import / Export buttons */
    importExportHolder = new Composite(buttonHolder, SWT.NONE);
    importExportHolder.setLayout(LayoutShop.createGridLayout(2, 5, 5, 5));
    importExportHolder.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

    /** Import GlobalSettings from file */
    importSettingsButton = new Button(importExportHolder, SWT.NONE);
    importSettingsButton.setText(GUI.i18n.getTranslation("MENU_IMPORT") + "...");
    importSettingsButton.setFont(FontShop.dialogFont);
    setButtonLayoutData(importSettingsButton);
    importSettingsButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        boolean success = eventManager.actionImportSettings(getShell());

        /** Exit settings dialog upon success */
        if (success)
          buttonPressed(IDialogConstants.CANCEL_ID);
      }
    });

    /** Export GlobalSettings into file */
    exportSettingsButton = new Button(importExportHolder, SWT.NONE);
    exportSettingsButton.setFont(FontShop.dialogFont);
    exportSettingsButton.setText(GUI.i18n.getTranslation("MENU_EXPORT") + "...");
    setButtonLayoutData(exportSettingsButton);
    exportSettingsButton.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {

        /** First save Settings */
        saveSettings();

        /** Export */
        eventManager.actionExportSettings(getShell());
      }
    });

    /** Composite to store Ok / Cancel buttons */
    Composite okCancelHolder = new Composite(buttonHolder, SWT.NONE);
    okCancelHolder.setLayout(LayoutShop.createGridLayout(2, 0, 5, 5));
    okCancelHolder.setLayoutData(new GridData(SWT.END, SWT.TOP, false, false));

    /** Button order on Mac is different */
    if (GUI.display.getDismissalAlignment() == SWT.RIGHT) {

      /** Create OK / Cancel Buttons */
      createButton(okCancelHolder, IDialogConstants.CANCEL_ID, GUI.i18n.getTranslation("BUTTON_CANCLE"), false).setFont(FontShop.dialogFont);
      createButton(okCancelHolder, IDialogConstants.OK_ID, GUI.i18n.getTranslation("BUTTON_OK"), true).setFont(FontShop.dialogFont);
    } else {

      /** Create OK / Cancel Buttons */
      createButton(okCancelHolder, IDialogConstants.OK_ID, GUI.i18n.getTranslation("BUTTON_OK"), true).setFont(FontShop.dialogFont);
      createButton(okCancelHolder, IDialogConstants.CANCEL_ID, GUI.i18n.getTranslation("BUTTON_CANCLE"), false).setFont(FontShop.dialogFont);
    }

    return buttonHolder;
  }

  /**
   * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite)
   */
  protected Control createDialogArea(Composite parent) {

    /** Composite to hold all components */
    Composite baseComposite = (Composite) super.createDialogArea(parent);
    baseComposite.setLayout(LayoutShop.createGridLayout(2, 0, 5, 15, 0, false));
    baseComposite.setLayoutData(LayoutDataShop.createGridData(GridData.FILL_BOTH, 1));

    /** Composite to hold the tree and filter */
    Composite treeHolder = new Composite(baseComposite, SWT.NONE);
    treeHolder.setLayoutData(LayoutDataShop.createGridData(GridData.FILL_VERTICAL, 1, convertHorizontalDLUsToPixels(140)));
    treeHolder.setLayout(LayoutShop.createGridLayout(1, 5, 0));

    /** Composite to hold filter controls above the Tree */
    final Composite filterHolder = new Composite(treeHolder, SWT.BORDER);
    filterHolder.setLayout(LayoutShop.createGridLayout(2, 0, 0, 0, 1, false));
    filterHolder.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
    filterHolder.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));

    /** Add some left-margin for platforms other than Windows */
    if (!GlobalSettings.isWindows())
      ((GridLayout) filterHolder.getLayout()).marginLeft = 3;

    /** Input to Filter preferences pages */
    filterText = new Text(filterHolder, SWT.NONE);
    filterText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true));
    filterText.setFont(FontShop.dialogFont);
    filterText.setText(GUI.i18n.getTranslation("BUTTON_SEARCH"));
    filterText.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));
    filterText.setForeground(GUI.display.getSystemColor(SWT.COLOR_DARK_GRAY));
    filterText.addModifyListener(new ModifyListener() {
      public void modifyText(ModifyEvent e) {
        if (!blockFilter)
          filterTree(filterText.getText());
      }
    });

    /** Pressing ESC while in Filter Modus will clear the Filter */
    filterText.addTraverseListener(new TraverseListener() {
      public void keyTraversed(TraverseEvent e) {

        /** Clear Filter on ESC */
        if (e.detail == SWT.TRAVERSE_ESCAPE && StringShop.isset(filterText.getText())) {
          filterText.setText("");
          e.doit = false;
        }

        /** Focus Tree on ENTER */
        else if (e.detail == SWT.TRAVERSE_RETURN) {

          /** Focus since Items are present */
          if (tree.getItemCount() > 0)
            tree.setFocus();

          /** Clear since Items are not present */
          else
            filterText.setText("");

          e.doit = false;
        }
      }
    });

    /** On Focus, clear Text and reset Foreground */
    filterText.addFocusListener(new FocusAdapter() {
      public void focusGained(FocusEvent e) {
        if (isFirstFilterInputFocus) {
          blockFilter = true;
          isFirstFilterInputFocus = false;
          filterText.setText("");
          filterText.setForeground(null);
          blockFilter = false;
        }
      }
    });

    /** Tweak Filter Input */
    WidgetShop.tweakTextWidget(filterText);

    /** ToolBar for the Filter-Clear Button */
    ToolBar clearBar = new ToolBar(filterHolder, SWT.FLAT);
    clearBar.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));
    clearBar.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, true));

    /** Button to Clear the Filter */
    clearFilter = new ToolItem(clearBar, SWT.PUSH);
    clearFilter.setImage(PaintShop.loadImage("/img/icons/closesearch.gif"));
    clearFilter.setToolTipText(GUI.i18n.getTranslation("BUTTON_CLEAR"));
    clearFilter.addDisposeListener(DisposeListenerImpl.getInstance());
    clearFilter.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        if (!isFirstFilterInputFocus && StringShop.isset(filterText.getText()))
          filterText.setText("");
      }
    });

    /** Tree holding all categorys for settings */
    tree = new Tree(treeHolder, SWT.BORDER);
    tree.setFont(FontShop.dialogFont);
    tree.setFocus();
    tree.setLayoutData(new GridData(GridData.FILL_BOTH));
    tree.addSelectionListener(new SelectionAdapter() {
      public void widgetSelected(SelectionEvent e) {
        handleTreeItemSelect();
      }
    });

    /** Expand / Collapse item on pressed enter key */
    tree.addKeyListener(new KeyAdapter() {
      public void keyPressed(KeyEvent e) {
        TreeItem selectedItem = tree.getSelection()[0];
        if (e.keyCode == SWT.CR && selectedItem.getItemCount() > 0)
          selectedItem.setExpanded(!selectedItem.getExpanded());
      }
    });

    /** Expand / Collapse selection on double click */
    tree.addListener(SWT.MouseDoubleClick, new Listener() {
      public void handleEvent(Event event) {
        if (tree.getSelectionCount() > 0) {
          Rectangle clickedRect = event.getBounds();
          Rectangle selectedRect = tree.getSelection()[0].getBounds();

          /** Only handle event, if Mouse is over treeitem */
          if (selectedRect.contains(clickedRect.x, clickedRect.y))
            tree.getSelection()[0].setExpanded(!tree.getSelection()[0].getExpanded());
        }
      }
    });

    /** Create the Properties by Filtering with no Value */
    filterTree(null);

    /** Composite holding the content page to change settings */
    contentHolder = new Composite(baseComposite, SWT.NONE);
    contentHolder.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.VERTICAL_ALIGN_BEGINNING));
    contentHolder.setLayout(LayoutShop.createGridLayout(1, 5, 0));

    /** Composite holding the title of the preference page */
    prefTitleHolder = new Composite(contentHolder, SWT.NONE);

    /** GridLayout for the Title Composite of preferences page */
    GridLayout prefTitleHolderLayout = new GridLayout(2, false);
    prefTitleHolderLayout.marginWidth = 1;
    prefTitleHolderLayout.marginHeight = 2;
    prefTitleHolderLayout.marginLeft = 4;
    prefTitleHolder.setLayout(prefTitleHolderLayout);

    prefTitleHolder.setLayoutData(LayoutDataShop.createGridData(GridData.FILL_HORIZONTAL, 2));
    prefTitleHolder.setBackground(GUI.display.getSystemColor(SWT.COLOR_WHITE));

    /** Change painting of the composite holding the title */
    prefTitleHolder.addPaintListener(new PaintListener() {
      public void paintControl(PaintEvent e) {
        e.gc.setForeground(GUI.display.getSystemColor(SWT.COLOR_DARK_GRAY));
        Rectangle bounds = prefTitleHolder.getClientArea();
        bounds.height -= 2;
        bounds.width -= 1;
        e.gc.drawRectangle(bounds);
      }
    });

    /** Select an element in the tree */
    setTreeSelection(lastOpenedPropertyPage);
    handleTreeItemSelect();

    /** Seperate from OK and Cancel Button */
    Label seperator = new Label(baseComposite, SWT.HORIZONTAL | SWT.SEPARATOR);
    seperator.setLayoutData(LayoutDataShop.createGridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.FILL_HORIZONTAL, 2));

    /** If shown, scroll the Bar back to start */
    if (WidgetShop.isset(tree.getHorizontalBar()))
      tree.getHorizontalBar().setSelection(0);

    return contentHolder;
  }

  /**
   * @see org.eclipse.jface.window.Window#getShellStyle()
   */
  protected int getShellStyle() {
    int style = SWT.TITLE | SWT.BORDER | SWT.RESIZE | SWT.APPLICATION_MODAL | getDefaultOrientation();

    /** Follow Apple's Human Interface Guidelines for Application Modal Dialogs */
    if (!GlobalSettings.isMac())
      style |= SWT.CLOSE;

    return style;
  }

  /**
   * @see org.eclipse.jface.dialogs.Dialog#initializeBounds()
   */
  protected void initializeBounds() {
    initializeBounds(true);
  }

  /**
   * Set size and location of the dialog
   * 
   * @param updateLocation If TRUE also update the location of the dialog
   */
  protected void initializeBounds(boolean updateLocation) {

    /** Define Shell Bounds */
    Point currentSize = getShell().getSize();
    Point bestSize = getShell().computeSize(convertHorizontalDLUsToPixels(dialogMinWidth), SWT.DEFAULT);

    /** Calculate new location or use old one */
    Point location = (updateLocation == true) ? getInitialLocation(bestSize) : getShell().getLocation();

    /** Only change if dialog needs to become bigger */
    if (updateLocation && bestSize.y > currentSize.y)
      getShell().setBounds(location.x, location.y, bestSize.x, bestSize.y);
    else if (bestSize.y > currentSize.y)
      getShell().setSize(bestSize.x, bestSize.y);

    /** Also define minimium size */
    getShell().setMinimumSize(bestSize.x, bestSize.y);
  }

  /**
   * Set the layout data of the button to a GridData with appropriate widths
   * This method was slightly modified so that it is not setting a heightHint.
   * 
   * @param button The button to layout
   */
  protected void setButtonLayoutData(Button button) {
    GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
    int widthHint = convertHorizontalDLUsToPixels(IDialogConstants.BUTTON_WIDTH);
    data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).x);
    button.setLayoutData(data);
  }

  /**
   * Filter the Tree containing all Property-Pages by the given Value. All
   * Property-Pages not matching the Filter, will be removed. If the
   * Filter-Value NULL or an empty String, all Property-Pages will be shown.
   * 
   * @param filterText The Value to use for Filtering.
   */
  void filterTree(String filterText) {
    boolean showAll = !StringShop.isset(filterText);

    /** Remember Selection if given */
    String selectionText = null;
    if (tree.getSelectionCount() > 0)
      selectionText = tree.getSelection()[0].getText();

    /** Clear the Items from the Tree */
    if (tree.getItemCount() > 0)
      tree.removeAll();

    /** Filter and retrieve Results */
    ArrayList result = showAll ? null : propertiesFilter.filter(filterText);

    /** Category: Workbench */
    if (showAll || result.contains("MENU_WORKBENCH")) {
      workbench = new TreeItem(tree, SWT.NONE);
      workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));

      if (!showAll)
        workbench.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Colors */
    if (showAll || result.contains("MENU_COLORS")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      colors = new TreeItem(workbench, SWT.NONE);
      colors.setText(GUI.i18n.getTranslation("MENU_COLORS"));

      if (!showAll)
        colors.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Font */
    if (showAll || result.contains("MENU_FONT")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      font = new TreeItem(workbench, SWT.NONE);
      font.setText(GUI.i18n.getTranslation("MENU_FONT"));

      if (!showAll)
        font.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Hotkeys */
    if (showAll || result.contains("MENU_HOTKEYS")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      hotkeys = new TreeItem(workbench, SWT.NONE);
      hotkeys.setText(GUI.i18n.getTranslation("MENU_HOTKEYS"));

      if (!showAll)
        hotkeys.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Language */
    if (showAll || result.contains("MENU_LANGUAGE")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      language = new TreeItem(workbench, SWT.NONE);
      language.setText(GUI.i18n.getTranslation("MENU_LANGUAGE"));

      if (!showAll)
        language.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Sort Order */
    if (showAll || result.contains("LABEL_SORT_ORDER")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      sortOrder = new TreeItem(workbench, SWT.NONE);
      sortOrder.setText(GUI.i18n.getTranslation("LABEL_SORT_ORDER"));

      if (!showAll)
        sortOrder.setFont(FontShop.dialogBoldFont);
    }

    /** Category: View */
    if (showAll || result.contains("MENU_WINDOW")) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      view = new TreeItem(workbench, SWT.NONE);
      view.setText(GUI.i18n.getTranslation("MENU_WINDOW"));

      if (!showAll)
        view.setFont(FontShop.dialogBoldFont);
    }

    /** Category: System Tray (if supported) */
    if (GlobalSettings.useSystemTray() && (showAll || result.contains("GROUP_TRAY"))) {

      /** Workbench as Parent is required */
      if (!WidgetShop.isset(workbench)) {
        workbench = new TreeItem(tree, SWT.NONE);
        workbench.setText(GUI.i18n.getTranslation("MENU_WORKBENCH"));
      }

      /** View as Parent is required */
      if (!WidgetShop.isset(view)) {
        view = new TreeItem(workbench, SWT.NONE);
        view.setText(GUI.i18n.getTranslation("MENU_WINDOW"));
      }

      systemTray = new TreeItem(view, SWT.NONE);
      systemTray.setText(GUI.i18n.getTranslation("GROUP_TRAY"));

      if (!showAll)
        systemTray.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Blogger */
    if (showAll || result.contains("MENU_BLOGGER")) {
      blogger = new TreeItem(tree, SWT.NONE);
      blogger.setText(GUI.i18n.getTranslation("MENU_BLOGGER"));

      if (!showAll)
        blogger.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Browser */
    if (showAll || result.contains("MENU_BROWSER")) {
      browser = new TreeItem(tree, SWT.NONE);
      browser.setText(GUI.i18n.getTranslation("MENU_BROWSER"));

      if (!showAll)
        browser.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Favorites */
    if (showAll || result.contains("HEADER_RSS_FAVORITES")) {
      favorites = new TreeItem(tree, SWT.NONE);
      favorites.setText(GUI.i18n.getTranslation("HEADER_RSS_FAVORITES"));

      if (!showAll)
        favorites.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Format NewsTip mail */
    if (showAll || result.contains("MENU_NEWSTIP_MAIL")) {
      formatNewsTipMail = new TreeItem(tree, SWT.NONE);
      formatNewsTipMail.setText(GUI.i18n.getTranslation("MENU_NEWSTIP_MAIL"));

      if (!showAll)
        formatNewsTipMail.setFont(FontShop.dialogBoldFont);
    }

    /** Category: Connection */
    if (showAll || result.contains("MENU_CONNECTION")) {
      connection = new TreeItem(tree, SWT.NONE);
      connection.setText(GUI.i18n.getTranslation("MENU_CONNECTION"));

      if (!showAll)
        connection.setFont(FontShop.dialogBoldFont);
    }

    /** Explicitly show Workbench expanded */
    if (WidgetShop.isset(workbench))
      workbench.setExpanded(true);

    /** Explicitly show View expanded */
    if (GlobalSettings.useSystemTray() && WidgetShop.isset(view))
      view.setExpanded(true);

    /** Restore Selection if given */
    if (StringShop.isset(selectionText))
      restoreSelection(selectionText, tree.getItems());
  }

  /** Handle selection of items in the tree and create new PropertyPage */
  void handleTreeItemSelect() {

    /** Return if no item is selected */
    if (tree.getSelection().length == 0)
      return;

    /** Check if Selection did not Change */
    if (tree.getSelection()[0].getText().equals(lastSelectedItemText))
      return;

    /** Remember the Selection */
    lastSelectedItemText = tree.getSelection()[0].getText();

    /** Workbench */
    if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_WORKBENCH"))) {
      createWorkbenchProps();
      lastOpenedPropertyPage = 0;
    }

    /** Colors */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_COLORS"))) {
      createColorProps();
      lastOpenedPropertyPage = 1;
    }

    /** Fonts */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_FONT"))) {
      createFontProps();
      lastOpenedPropertyPage = 2;
    }

    /** Hotkeys */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_HOTKEYS"))) {
      createHotkeysProps();
      lastOpenedPropertyPage = 3;
    }

    /** Language */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_LANGUAGE"))) {
      createLanguageProps();
      lastOpenedPropertyPage = 4;
    }

    /** Sort Order */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("LABEL_SORT_ORDER"))) {
      createSortOrderProps();
      lastOpenedPropertyPage = 5;
    }

    /** View */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_WINDOW"))) {
      createViewProps();
      lastOpenedPropertyPage = 6;
    }

    /** System Tray */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("GROUP_TRAY"))) {
      createSystemTrayProps();
      lastOpenedPropertyPage = 7;
    }

    /** Blogger */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_BLOGGER"))) {
      createBloggerProps();
      lastOpenedPropertyPage = 8;
    }

    /** Browser */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_BROWSER"))) {
      createBrowserProps();
      lastOpenedPropertyPage = 9;
    }

    /** Favorites */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("HEADER_RSS_FAVORITES"))) {
      createFavoritesProps();
      lastOpenedPropertyPage = 10;
    }

    /** Format NewsTip Mail */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_NEWSTIP_MAIL"))) {
      createMailProps();
      lastOpenedPropertyPage = 11;
    }

    /** Connection */
    else if (tree.getSelection()[0].getText().equals(GUI.i18n.getTranslation("MENU_CONNECTION"))) {
      createConnectionProps();
      lastOpenedPropertyPage = 12;
    }

    /** Update layout */
    contentHolder.layout();

    /** Update shell size */
    initializeBounds(false);
  }

  /**
   * Update all Settings and Save them.
   */
  void saveSettings() {

    /** First apply active Page */
    activePropertyPage.applyButtonPressed();

    /** Save Properties */
    PropertyPage.getPropertyChangeManager().saveProperties();

    /**
     * Save Shell bounds if RSSOwl is not minimized to tray<br>
     */
    if (rssOwlGui.getRSSOwlSystray() == null || !rssOwlGui.getRSSOwlSystray().isMinimizedToTray()) {
      GlobalSettings.shellBounds = GUI.shell.getBounds();
      GlobalSettings.isShellMaximized = GUI.shell.getMaximized();
    }

    /** Settings Files and Backup */
    File settings = new File(GlobalSettings.RSSOWL_SETTINGS_FILE);
    File backup = new File(GlobalSettings.RSSOWL_SETTINGS_BACKUP_FILE);

    /** Save all settings to the user.xml file */
    new SettingsSaver(rssOwlGui).saveUserSettings(false);

    /** Backup the settings file */
    try {
      SimpleFileShop.copy(settings, backup);
    } catch (IOException e) {
      GUI.logger.log("saveSettings()", e);
    }
  }
}