/*   **********************************************************************  **
 **   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;

import net.sourceforge.rssowl.controller.dialog.ToolBarDialog;
import net.sourceforge.rssowl.controller.statusline.LoadJob;
import net.sourceforge.rssowl.controller.statusline.StatusLine;
import net.sourceforge.rssowl.controller.thread.AggregationLoader;
import net.sourceforge.rssowl.controller.thread.AmphetaRateThread;
import net.sourceforge.rssowl.controller.thread.FeedAutoUpdater;
import net.sourceforge.rssowl.controller.thread.FeedLoader;
import net.sourceforge.rssowl.controller.thread.FeedQueueLoader;
import net.sourceforge.rssowl.controller.thread.SettingsManager;
import net.sourceforge.rssowl.controller.thread.ShutdownHook;
import net.sourceforge.rssowl.controller.thread.StartupManager;
import net.sourceforge.rssowl.controller.thread.UpdateManager;
import net.sourceforge.rssowl.controller.tray.SystemTray;
import net.sourceforge.rssowl.dao.SettingsLoader;
import net.sourceforge.rssowl.dao.SettingsSaver;
import net.sourceforge.rssowl.model.Category;
import net.sourceforge.rssowl.model.Channel;
import net.sourceforge.rssowl.util.CryptoManager;
import net.sourceforge.rssowl.util.GlobalSettings;
import net.sourceforge.rssowl.util.LoggerImpl;
import net.sourceforge.rssowl.util.archive.ArchiveManager;
import net.sourceforge.rssowl.util.archive.FeedCacheManager;
import net.sourceforge.rssowl.util.i18n.ITranslatable;
import net.sourceforge.rssowl.util.i18n.LanguageDetector;
import net.sourceforge.rssowl.util.i18n.RSSOwlI18n;
import net.sourceforge.rssowl.util.search.SearchDefinition;
import net.sourceforge.rssowl.util.shop.BrowserShop;
import net.sourceforge.rssowl.util.shop.FontShop;
import net.sourceforge.rssowl.util.shop.HotkeyShop;
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.StringShop;
import net.sourceforge.rssowl.util.shop.URLShop;
import net.sourceforge.rssowl.util.shop.WidgetShop;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.HelpEvent;
import org.eclipse.swt.events.HelpListener;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;

import java.text.Collator;
import java.util.Locale;
import java.util.TreeSet;

/**
 * This is RSSOwl's Main controller. All GUI Elements are registered in here and
 * getter Methods are provided so that other Classes are able to access and use
 * them. All settings are loaded in here.
 * 
 * @author <a href="mailto:bpasero@rssowl.org">Benjamin Pasero </a>
 * @version 1.2.3
 */
public class GUI implements ITranslatable {

  /** A lot of other obj have to access display */
  public static Display display;

  /** Internationalization for RSSOwl */
  public static RSSOwlI18n i18n;

  /** Flag is set to TRUE when RSSOwl is exiting */
  public static boolean isClosing = false;

  /** Log all catched Exceptions */
  public static LoggerImpl logger = new LoggerImpl();

  /** For classes who needs to access the MainController */
  public static GUI rssOwlGui;

  /** A lot of other obj have to access shell */
  public static Shell shell;

  private Composite boxContainer;
  private SashForm holdContentSash;
  private SashForm holdNewsSash;
  private AmphetaRateThread rssOwlAmphetaRate;
  private CryptoManager rssOwlCryptoManager;
  private FeedAutoUpdater rssOwlFeedAutoUpdater;
  private FeedQueueLoader rssOwlFeedQueueLoader;
  private RSSOwlMenu rssOwlMenu;
  private NewsText rssOwlNewsText;
  private Quickview rssOwlQuickView;
  private SystemTray rssOwlSystray;
  private SettingsManager settingsManager;
  ArchiveManager archiveManager;
  EventManager eventManager;
  FavoritesTree rssOwlFavoritesTree;
  FeedCacheManager rssOwlFeedCacheManager;
  NewsTabFolder rssOwlNewsTabFolder;
  StatusLine rssOwlStatusLine;

  /**
   * Instantiate a new GUI
   * 
   * @param display The display
   * @param splashShell The shell holding the splash
   */
  public GUI(Display display, Shell splashShell) {

    /** Init fields */
    GUI.display = display;
    GUI.rssOwlGui = this;
    archiveManager = new ArchiveManager();
    rssOwlFeedQueueLoader = new FeedQueueLoader();

    /** Startup process */
    startUp();

    /** Dispose the Splashscreen */
    splashShell.dispose();
  }

  /**
   * This method is called to check if RSSOwl is still alive or was just closed
   * by the user.
   * 
   * @return boolean TRUE if RSSOwl is still running
   */
  public static boolean isAlive() {
    return (!display.isDisposed() && !shell.isDisposed() && !isClosing);
  }

  /**
   * This method is called from the Shutdown Hook Thread in case RSSOwl was shut
   * down not the normal way (e.g. the OS is shutting down). <br />
   * It is not guaranteed, that this method will be executed in any case.
   */
  public void abnormalShutDown() {

    /** Shutdown the archive manager */
    archiveManager.flush();

    /** Save sensible user data */
    rssOwlCryptoManager.saveData();

    /** Save settings */
    new SettingsSaver(this).saveUserSettings(true);

    /** Exit cache manager */
    rssOwlFeedCacheManager.shutdown();

    /** Shutdown Server */
    StartupManager.stopServer();
  }

  /**
   * Change the window layout between 2 and 3 column window layout
   */
  public void changeWindowLayout() {

    /** Create 3-column layout of main controlls */
    if (GlobalSettings.isThreeColumnGUI)
      holdNewsSash.setOrientation(SWT.HORIZONTAL);

    /** Create 2-column layout of main controlls */
    else
      holdNewsSash.setOrientation(SWT.VERTICAL);
  }

  /**
   * Display the selected newsfeed in the TabFolder
   * 
   * @param rssChannel The newsfeed to display
   * @param url URL / Path to the RSS XML
   * @param searchDefinition Pattern and Scope of the Search.
   * @param reselectNews Wether to reselect an already selected news
   * @param displayMode One of the supported display modes
   */
  public void displayNewsfeed(Channel rssChannel, String url, SearchDefinition searchDefinition, boolean reselectNews, int displayMode) {
    rssOwlNewsTabFolder.displayNewsfeed(rssChannel, url, searchDefinition, reselectNews, displayMode);
  }

  /**
   * Enable / Disable Systray Icon for RSSOwl
   * 
   * @param enabled TRUE if enabled
   */
  public void enableSystrayIcon(boolean enabled) {

    /** Activate Systray */
    if (enabled) {

      /** Check if unread news are available */
      SystemTray.trayIsTeasing = rssOwlFavoritesTree.getTreeHasUnreadFavs();

      /** Create Systray */
      rssOwlSystray = new SystemTray(display, shell, this);
    }

    /** Disable Systray */
    else if (rssOwlSystray != null) {
      rssOwlSystray.disable();
    }
  }

  /**
   * Get the archive manager for RSSOwl
   * 
   * @return Returns the archiveManager.
   */
  public ArchiveManager getArchiveManager() {
    return archiveManager;
  }

  /**
   * Get the SashForm containing the Content.
   * 
   * @return SashForm containing the Content.
   */
  public SashForm getContentSash() {
    return holdContentSash;
  }

  /**
   * Get the event manager for RSSOwl.
   * 
   * @return Returns the eventManager.
   */
  public EventManager getEventManager() {
    return eventManager;
  }

  /**
   * Get the feed cache manager
   * 
   * @return RSSOwlFeedCachManager The cache manager
   */
  public FeedCacheManager getFeedCacheManager() {
    return rssOwlFeedCacheManager;
  }

  /**
   * Get the SashForm containing the News.
   * 
   * @return SashForm containing the News.
   */
  public SashForm getNewsSash() {
    return holdNewsSash;
  }

  /**
   * Let other objects access this thread
   * 
   * @return AmphetaRateThread Submit Thread
   */
  public AmphetaRateThread getRSSOwlAmphetaRate() {
    return rssOwlAmphetaRate;
  }

  /**
   * Method to let other obj access this object
   * 
   * @return FavoritesTree The tree holding all favorites
   */
  public FavoritesTree getRSSOwlFavoritesTree() {
    return rssOwlFavoritesTree;
  }

  /**
   * Let other objects access this thread
   * 
   * @return Returns the rssOwlFeedQueueLoader.
   */
  public FeedQueueLoader getRSSOwlFeedQueueLoader() {
    return rssOwlFeedQueueLoader;
  }

  /**
   * Method to let other obj access this object
   * 
   * @return RSSOwlMenu
   */
  public RSSOwlMenu getRSSOwlMenu() {
    return rssOwlMenu;
  }

  /**
   * Get the NewsTabFolder displaying newsfeeds
   * 
   * @return NewsTabFolder
   */
  public NewsTabFolder getRSSOwlNewsTabFolder() {
    return rssOwlNewsTabFolder;
  }

  /**
   * Get the NewsText composite
   * 
   * @return NewsText
   */
  public NewsText getRSSOwlNewsText() {
    return rssOwlNewsText;
  }

  /**
   * Get the RSSOwl quickview control
   * 
   * @return Quickview
   */
  public Quickview getRSSOwlQuickview() {
    return rssOwlQuickView;
  }

  /**
   * Get the StatusLine control
   * 
   * @return Returns the rssOwlStatusLine.
   */
  public StatusLine getRSSOwlStatusLine() {
    return rssOwlStatusLine;
  }

  /**
   * Get the SystemTray object
   * 
   * @return SystemTray The system tray implementation for RSSOwl
   */
  public SystemTray getRSSOwlSystray() {
    return rssOwlSystray;
  }

  /**
   * Check if RSSOwl is currently busy loading a newsfeed or aggregation.
   * 
   * @return boolean TRUE if RSSOwl is busy loading
   */
  public boolean isBusyLoading() {
    return rssOwlStatusLine.isBusyLoading();
  }

  /**
   * Load a News feed. The loading is done in a Thread to enable cancle of the
   * loading via a button in the status line.
   * 
   * @param url URL / Path to the feed
   * @param searchDefinition Pattern and Scope of the Search.
   * @param displayNewsfeed TRUE if newsfeed should be displayed
   * @param reselectNews Wether to reselect a selected news
   * @param displayMode One of the supported displaymodes
   */
  public void loadNewsFeed(String url, SearchDefinition searchDefinition, boolean displayNewsfeed, boolean reselectNews, int displayMode) {

    /** URL must not be empty */
    if (!StringShop.isset(url))
      return;

    int statusLineStyle = StatusLine.LOAD;
    boolean performSearch = StringShop.isset(searchDefinition.getPattern());

    /** Status Line Style: Reload */
    if (!displayNewsfeed)
      statusLineStyle = StatusLine.RELOAD;

    /** Status Line Style: Search */
    else if (performSearch)
      statusLineStyle = StatusLine.SEARCH;

    /** Create a new feed loader Thread */
    FeedLoader feedLoader = new FeedLoader(url, searchDefinition, displayNewsfeed, reselectNews, displayMode);

    /** Create a new LoadJob for the status line to display */
    LoadJob loadJob = new LoadJob(url, Category.getTitleForLink(url), false, statusLineStyle, feedLoader);
    rssOwlStatusLine.insertJob(loadJob);
    rssOwlStatusLine.addEntireLoad(1);

    /** Apply load job to loading thread */
    feedLoader.setRSSOwlLoadJob(loadJob);

    /** Start the Thread to load the XML */
    feedLoader.startThread();
  }

  /**
   * Re-Load a news feed
   * 
   * @param url URL to the newsfeed
   */
  public void reloadNewsFeed(String url) {

    /** Check if the tab is opened in the tabfolder */
    boolean feedIsOpened = (rssOwlNewsTabFolder.getFeedTabItem(url) != null);

    /** Remove feed from cache */
    rssOwlFeedCacheManager.unCacheNewsfeed(url, false);

    /** Reload feed */
    loadNewsFeed(url, SearchDefinition.NO_SEARCH, feedIsOpened, true, NewsTabFolder.DISPLAY_MODE_NO_FOCUS);
  }

  /**
   * Restore the application window either from taskbar or the tray.
   */
  public void restoreWindow() {

    /** RSSOwl is minimized to Tray */
    if (GlobalSettings.useSystemTray() && rssOwlSystray != null && rssOwlSystray.isMinimizedToTray())
      rssOwlSystray.restoreWindow();

    /** RSSOwl is not active */
    else {
      shell.forceActive();
      shell.setMinimized(false);
    }
  }

  /**
   * Set the state of the favorites Tree with setting the maximized control of
   * the holdContentSash.
   * 
   * @param minimize If TRUE minimize the favorites tree
   * @param save If TRUE save the state to GlobalSettings
   */
  public void setFavoritesMinimized(boolean minimize, boolean save) {
    boolean isMinimized = (holdContentSash.getMaximizedControl() != null);

    /** Only change if there is a difference */
    if (isMinimized != minimize && WidgetShop.isset(holdContentSash)) {

      /** First ensure that the Rename Box is disposed if open */
      rssOwlFavoritesTree.stopRenaming();

      /** Set new Maximized Control */
      holdContentSash.setMaximizedControl((minimize == true) ? holdNewsSash : null);

      /** Update Menu */
      rssOwlMenu.setFavoritesTreeMenuEnabled(!minimize);

      /** Update Global Settings */
      if (save) {
        GlobalSettings.isFavoritesTreeShown = !minimize;
        settingsManager.requestSave();
      }
    }
  }

  /** Update all controlls text with i18n */
  public void updateI18N() {

    /** Update I18N in the menuStructure */
    rssOwlMenu.updateI18N();

    /** Update I18N in Quickview */
    rssOwlQuickView.updateI18N();

    /** Update I18N in url tree */
    rssOwlFavoritesTree.updateI18N();

    /** Update I18N in NewsTabFolder */
    rssOwlNewsTabFolder.updateI18N();

    /** Update I18N in NewsText ViewForm */
    rssOwlNewsText.updateI18N();

    /** Update I18N in Status Line */
    rssOwlStatusLine.updateI18N();

    /** Update I18N for systray menu */
    if (rssOwlSystray != null)
      rssOwlSystray.updateI18N();

    /** Update a possible open Toolbar Dialog */
    if (eventManager.toolBarDialog != null)
      eventManager.toolBarDialog.updateI18N();

    /** Update Shell Title */
    shell.setText(WidgetShop.getShellTitle());

    /** Layout all */
    LayoutShop.setLayoutForAll(shell);
  }

  /** Init all components */
  private void initComponents() {

    /**
     * Most Mac-Users will quit RSSOwl using the application menu. This
     * mechanism does not trigger the close-event for the Shell. Therefor save
     * the Shell Bounds as soon as the Display gets disposed.
     */
    if (GlobalSettings.isMac()) {
      display.addListener(SWT.Dispose, new Listener() {
        public void handleEvent(Event event) {
          if (WidgetShop.isset(shell))
            GlobalSettings.shellBounds = shell.getBounds();
        }
      });
    }

    /** Build a new shell that holds the application */
    shell = new Shell(display);
    shell.setLayout(LayoutShop.createGridLayout(1, 0, 0, 3));
    shell.setText(WidgetShop.getShellTitle());

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

    /** Display the TutorialBrowser if the user requests help */
    shell.addHelpListener(new HelpListener() {
      public void helpRequested(HelpEvent e) {
        new TutorialBrowser(display, shell, rssOwlGui).show();
      }
    });

    /** Save favorites before quit */
    shell.addDisposeListener(new DisposeListener() {
      public void widgetDisposed(DisposeEvent e) {

        /** Remove Shell from View on Exit (not working on Mac) */
        if (!GlobalSettings.isMac() && !shell.isDisposed() && shell.isVisible())
          shell.setVisible(false);

        /** Shutdown procedure */
        onDispose();
      }
    });

    /** Listen for close event to set isClosing flag */
    shell.addListener(SWT.Close, new Listener() {
      public void handleEvent(Event event) {
        onClose(event);
      }
    });

    /** Listen for iconify event */
    shell.addListener(SWT.Iconify, new Listener() {
      public void handleEvent(Event event) {
        onIconify();
      }
    });

    /** Create the event manager */
    eventManager = new EventManager(display, shell, this);

    /** Set the main menu */
    rssOwlMenu = new RSSOwlMenu(this, shell, eventManager);

    /** Set the RSS Quickview controll */
    rssOwlQuickView = new Quickview(this, shell, eventManager);

    /** Container for all boxes (Favorites, TabFolder and Newstext) */
    boxContainer = new Composite(shell, SWT.NONE);
    boxContainer.setLayout(LayoutShop.createGridLayout(1, 4, 2, 3));
    boxContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));

    /** SashForm holding all the MAIN Content Composites */
    holdContentSash = new SashForm(boxContainer, SWT.SMOOTH);
    holdContentSash.setLayoutData(LayoutDataShop.createGridData(GridData.FILL_BOTH, 4));

    /** RSSOwl Favorite Tree */
    rssOwlFavoritesTree = new FavoritesTree(display, shell, holdContentSash, this, eventManager);

    /** News Sash Form used for TabFolder and Newstext */
    int style = (GlobalSettings.isThreeColumnGUI == true) ? SWT.HORIZONTAL : SWT.VERTICAL;
    holdNewsSash = new SashForm(holdContentSash, style | SWT.SMOOTH);

    /** TabFolder for the news header */
    rssOwlNewsTabFolder = new NewsTabFolder(display, shell, holdNewsSash, this, eventManager);

    /** View of a News */
    rssOwlNewsText = new NewsText(display, shell, holdNewsSash, this, eventManager);

    /** Create control to display the status line */
    rssOwlStatusLine = new StatusLine(display, boxContainer, this);

    /** Sync controls with event manager */
    eventManager.syncControls();

    /** Init Tabfolder with empty tab state */
    rssOwlNewsTabFolder.updateTabFolderState();
  }

  /** Runs the event loop for RSSOwl */
  private void runEventLoop() {

    /**
     * This is not very good style, but I will catch any exception, to log and
     * display the message!
     */
    try {
      while (!shell.isDisposed()) {
        if (!display.readAndDispatch()) {
          display.sleep();
        }
      }
    } catch (Exception e) {

      /** Log and display Message */
      logger.log("runEventLoop (Unforseen Exception)", e);
      logger.logCritical("runEventLoop (Unforseen Exception)", e);

      /** Ask the user if he wants to send the report */
      int result = MessageBoxFactory.showMessage(shell, SWT.YES | SWT.NO | SWT.ICON_ERROR, i18n.getTranslation("MESSAGE_BOX_TITLE_ERROR"), i18n.getTranslation("ERROR_UNEXPECTED"));

      /** Call the mail application if the user wants to send the error report */
      if (result == SWT.YES)
        BrowserShop.openLink(URLShop.createErrorReport());
    }

    /** Dispose display */
    display.dispose();
  }

  /**
   * Startup process of RSSOwl (called once at start)
   */
  private void startUp() {

    /** Settings loader to get the user settings */
    SettingsLoader settingsLoader = new SettingsLoader(this);

    /** Crypto Manager for sensitive data */
    rssOwlCryptoManager = CryptoManager.getInstance();

    /** Start the ArchiveManager */
    archiveManager.startup();

    /** Assign new ShutdownHook as Shutdown Hook to Runtime */
    Runtime.getRuntime().addShutdownHook(new ShutdownHook(this));

    /** Update core settings of RSSOwl */
    updateCoreSettings(settingsLoader, false);

    /** Set the accelerators */
    HotkeyShop.initDefaultAccelerators();

    /** Init Icons */
    PaintShop.initIcons();

    /** Init Colors */
    PaintShop.initColors();

    /** New Cache Manager */
    rssOwlFeedCacheManager = new FeedCacheManager();

    /** Init all components */
    initComponents();

    /** Update user settings of RSSOwl */
    updateUserSettings(settingsLoader, false);

    /** Preselect: Welcome-Tab if first start */
    if (GlobalSettings.isWelcomeShown)
      rssOwlNewsTabFolder.showWelcomeTab();

    /** Check for a new RSSOwl Version */
    if (GlobalSettings.checkUpdate)
      new UpdateManager(this, false).start();

    /** Start the RSS queue loader thread */
    rssOwlFeedQueueLoader.startThread();

    /** Start the auto updater for the favorits */
    rssOwlFeedAutoUpdater = new FeedAutoUpdater(this);
    rssOwlFeedAutoUpdater.start();

    /** Start AmphetaRate thread */
    rssOwlAmphetaRate = new AmphetaRateThread();
    // rssOwlAmphetaRate.start();

    /** Start Settings Manager */
    settingsManager = SettingsManager.getInstance();
    settingsManager.start();

    /** Linux Bug: Popup on Tree is not appearing proberbly on selection */
    if (GlobalSettings.isLinux()) {
      rssOwlFavoritesTree.getFavoritesTree().setFocus();
      rssOwlFavoritesTree.getFavoritesTree().notifyListeners(SWT.Selection, new Event());
    }

    /** Place to system tray if user has set so */
    if (GlobalSettings.useSystemTray() && GlobalSettings.showSystrayIcon && GlobalSettings.trayOnStartup)
      shell.notifyListeners(SWT.Iconify, new Event());
  }

  /**
   * Called when the Shell is closed
   * 
   * @param event The occuring Event
   */
  void onClose(Event event) {
    onClose(event, false);
  }

  /**
   * Called when the Shell is closed. If boolean parameter forceExit is set to
   * TRUE, RSSOwl will exit, even if "Minimize to Tray on Exit" is set to TRUE.
   * This forced exit is called when the user has pressed the "Exit" menuitem
   * from the "File" menu.
   * 
   * @param event The occuring Event
   * @param forceExit If TRUE, force RSSOwl to exit
   */
  void onClose(Event event, boolean forceExit) {

    /** Move to system tray on exit if user has set so and forceExit is FALSE */
    if (GlobalSettings.useSystemTray() && GlobalSettings.showSystrayIcon && GlobalSettings.trayOnExit && !forceExit) {
      event.doit = false;

      /** Fire Iconify Event */
      shell.notifyListeners(SWT.Iconify, new Event());
      return;
    }

    /** Else: Exit application */
    isClosing = true;

    /** Save Shell bounds if RSSOwl is not minimized to tray */
    if (WidgetShop.isset(shell) && (rssOwlSystray == null || !rssOwlSystray.isMinimizedToTray())) {
      GlobalSettings.shellBounds = shell.getBounds();
      GlobalSettings.isShellMaximized = shell.getMaximized();
    }
  }

  /**
   * Called when the Shell is disposed
   */
  void onDispose() {

    /** Shutdown procedure */
    shutDown();
  }

  /**
   * Called when the Shell is Iconified
   */
  void onIconify() {

    /** Mark All Favorites read if set so */
    if (GlobalSettings.markAllReadOnMinimize)
      eventManager.actionMarkAllCategoriesRead();

    /** Reset Popup Time Lock if used */
    if (rssOwlSystray != null)
      rssOwlSystray.resetPopupTimeLock();
  }

  /** Open RSSOwl shell */
  void showGui() {

    /** Only open shell if user has not set trayOnStartup to TRUE */
    if (!GlobalSettings.useSystemTray() || !GlobalSettings.showSystrayIcon || !GlobalSettings.trayOnStartup)
      shell.open();

    /** Handle the argument that the user has passed to RSSOwl */
    if (StringShop.isset(RSSOwlLoader.feedArgument))
      eventManager.actionHandleSuppliedLink(RSSOwlLoader.feedArgument);

    /** Start the event loop to read and dispatch events */
    runEventLoop();
  }

  /**
   * Shut down RSSOwl and perform some actions before closing
   */
  void shutDown() {

    /** Interrupt ampheta rate submit thread */
    // rssOwlAmphetaRate.stopThread();
    
    /** Stop SettingsManager */
    settingsManager.stopThread();

    /** Stop the autoupdater */
    rssOwlFeedAutoUpdater.interrupt();

    /** Save settings */
    new SettingsSaver(this).saveUserSettings(false);

    /** Save sensible user data */
    rssOwlCryptoManager.saveData();

    /** Shutdown the archive manager */
    archiveManager.flush();

    /** Interrupt the RSS queue loader thread */
    rssOwlFeedQueueLoader.stopThread();

    /** Exit cache manager */
    rssOwlFeedCacheManager.shutdown();

    /** Shutdown Server */
    StartupManager.stopServer();

    /** Cleanup */
    FontShop.disposeFonts();
    PaintShop.disposeIcons();

    /** Clean Drag and Drop */
    rssOwlFavoritesTree.getRSSOwlFavoritesTreeDND().dispose();
    rssOwlNewsTabFolder.getRSSOwlNewsTabFolderDND().dispose();
  }

  /**
   * Update core settings of RSSOwl that need to be loaded first
   * 
   * @param settingsLoader The settings parser to use
   * @param isHotLoad TRUE if the settings are imported by the user from the
   * running program
   */
  void updateCoreSettings(SettingsLoader settingsLoader, boolean isHotLoad) {

    /** Load if welcome tab should show */
    GlobalSettings.isWelcomeShown = settingsLoader.getBoolean("isWelcomeShown", true);

    /** Language of RSSOwl */
    i18n = new RSSOwlI18n(new Locale(settingsLoader.getValue("language", "value", LanguageDetector.parseLanguage(Locale.getDefault()))));

    /** Fonts */
    FontShop.textFont = settingsLoader.loadFont(FontShop.textFont, "textfont", isHotLoad);
    FontShop.treeFont = settingsLoader.loadFont(FontShop.treeFont, "treefont", isHotLoad);
    FontShop.tableFont = settingsLoader.loadFont(FontShop.tableFont, "tablefont", isHotLoad);
    FontShop.headerFont = settingsLoader.loadFont(FontShop.headerFont, "headerfont", isHotLoad);
    FontShop.dialogFont = settingsLoader.loadFont(FontShop.dialogFont, "dialogfont", isHotLoad);

    /** Update fonts */
    FontShop.updateFonts();

    /** Link Color */
    GlobalSettings.linkColor = settingsLoader.loadColor("linkColor", new RGB(0, 0, 255));
    PaintShop.initLinkColor(display);

    /** URL Tree selection */
    if (!isHotLoad)
      GlobalSettings.selectedTreeItem = settingsLoader.getValue("treeSelection", null, "", false);

    /** Show systray icon */
    GlobalSettings.showSystrayIcon = settingsLoader.getBoolean("showSystrayIcon", false);

    /** Use internal browser for newstext */
    if (GlobalSettings.useInternalBrowser()) {
      if (!isHotLoad)
        GlobalSettings.useBrowserForNewsText = settingsLoader.getBoolean("useBrowserForNewsText", true);
    }

    /** Unsupported OS for internal browser */
    else {
      GlobalSettings.useBrowserForNewsText = false;
    }

    /** Use external browser */
    GlobalSettings.openBrowserExtern = settingsLoader.getBoolean("openBrowserExtern", false);

    /** Use three- or two-column GUI */
    GlobalSettings.isThreeColumnGUI = settingsLoader.getBoolean("isThreeColumnGUI", false);

    /** CTabItem settings */
    GlobalSettings.tabPositionIsTop = settingsLoader.getBoolean("tabPositionIsTop", true);
    GlobalSettings.displaySingleTab = settingsLoader.getBoolean("displaySingleTab", false);
    GlobalSettings.showTabCloseButton = settingsLoader.getBoolean("showTabCloseButton", true);

    /** Local Cache Settings */
    GlobalSettings.localCacheFeeds = settingsLoader.getBoolean("localCacheFeeds", true);

    /** Offline mode */
    if (!isHotLoad)
      GlobalSettings.workOffline = settingsLoader.getBoolean("workOffline", false);
  }

  /**
   * Update some user settings from the "user.xml"
   * 
   * @param settingsLoader The settings parser to use
   * @param isHotLoad TRUE if the settings are imported by the user from the
   * running program
   */
  void updateUserSettings(SettingsLoader settingsLoader, boolean isHotLoad) {

    /** Reset Hashtables / Lists */
    rssOwlFavoritesTree.getFavoritesTree().removeAll();
    Category.getRootCategory().reset();

    /** Proxy GlobalSettings */
    settingsLoader.loadProxySettings();

    /** Load the max. connection count */
    GlobalSettings.maxConnectionCount = Integer.parseInt(settingsLoader.getValue("maxConnectionCount", "value", "32"));

    /** Load the connection timeout */
    GlobalSettings.connectionTimeout = Integer.parseInt(settingsLoader.getValue("connectionTimeout", "value", "30"));

    /** Text Encoding */
    GlobalSettings.charEncoding = settingsLoader.getValue("encoding", "name", "UTF-8");

    /** Get the rssOwlFavTree holding all categorys and links */
    Category.createRootCategory();
    settingsLoader.loadFavorites();

    /** Load Categories and sort them */
    rssOwlFavoritesTree.buildFavoritesTree();

    /** Load sort order for news */
    GlobalSettings.sortOrder = settingsLoader.loadSortOrder();
    GlobalSettings.autoSortNews = settingsLoader.getBoolean("autoSortNews", true);

    /** Load favorites on startup if user set so */
    TreeSet favorites = new TreeSet(Collator.getInstance());
    Category.getRootCategory().getLoadOnStartupFavorites(favorites, Category.getRootCategory());

    /** There are favorites to load */
    if (favorites.size() > 0) {
      AggregationLoader multiLoader = new AggregationLoader(favorites, Category.getRootCategory(), this, "");
      multiLoader.loadFavorites(false);
    }

    /** Confirmations */
    GlobalSettings.confirmFavoriteDeletion = settingsLoader.getBoolean("confirmFavoriteDeletion", true);
    GlobalSettings.confirmCategoryDeletion = settingsLoader.getBoolean("confirmCategoryDeletion", true);
    GlobalSettings.confirmBlogrollDeletion = settingsLoader.getBoolean("confirmBlogrollDeletion", true);

    /** Tray GlobalSettings */
    GlobalSettings.trayOnStartup = settingsLoader.getBoolean("trayOnStartup", false);
    GlobalSettings.trayOnExit = settingsLoader.getBoolean("trayOnExit", false);
    GlobalSettings.showTrayPopup = settingsLoader.getBoolean("showTrayPopup", true);

    /** Window sizes and visibility */
    settingsLoader.loadShellBounds();

    /** Shell Properties */
    boolean isMaximized = GlobalSettings.isShellMaximized;
    Rectangle bounds = GlobalSettings.shellBounds;
    int monitorCount = display.getMonitors().length;

    /** Shell setup for one monitor */
    if (monitorCount <= 1) {

      /** Bounds */
      if (bounds != null && !isMaximized)
        shell.setBounds(bounds);

      /** Maximized State */
      if (isMaximized)
        shell.setMaximized(isMaximized);
    }

    /** Shell setup for two or more monitors */
    else {

      /** Bounds */
      if (bounds != null)
        shell.setBounds(bounds);

      /** Maximized State */
      if (isMaximized)
        shell.setMaximized(isMaximized);
    }

    /** Content and News Sash Weights */
    settingsLoader.loadSashWeights();
    holdContentSash.setWeights(GlobalSettings.contentSashWeights);
    holdNewsSash.setWeights(GlobalSettings.newsSashWeights);

    /** Show RSS Quickview */
    GlobalSettings.isQuickviewShown = settingsLoader.getBoolean("isQuickviewShown", true);
    rssOwlQuickView.setShowQuickview(GlobalSettings.isQuickviewShown, false);

    /** ToolBar - Create and Show */
    GlobalSettings.isToolBarShown = settingsLoader.getBoolean("isToolBarShown", true);
    GlobalSettings.useSmallIcons = settingsLoader.getBoolean("useSmallIcons", true);
    GlobalSettings.toolBarItems = settingsLoader.getValue("toolBarItems", "value", ToolBarDialog.getDefaultToolBarItems());
    GlobalSettings.toolBarMode = Integer.parseInt(settingsLoader.getValue("toolBarMode", "value", String.valueOf(Quickview.TOOLBAR_MODE_ICONS_TEXT)));
    rssOwlQuickView.createToolBar();
    rssOwlQuickView.setShowToolBar(GlobalSettings.isToolBarShown, false);

    /** Show Tree ToolBar */
    GlobalSettings.isTreeToolBarShown = settingsLoader.getBoolean("isTreeToolBarShown", true);
    rssOwlFavoritesTree.setShowToolBar(GlobalSettings.isTreeToolBarShown);

    /** Show Favorites */
    GlobalSettings.isFavoritesTreeShown = settingsLoader.getBoolean("isFavoritesTreeShown", true);
    setFavoritesMinimized(!GlobalSettings.isFavoritesTreeShown, false);
    rssOwlMenu.setFavoritesTreeMenuEnabled(GlobalSettings.isFavoritesTreeShown);

    /** Load state of "Link Tree with displayed Feed" */
    GlobalSettings.linkTreeWithTab = settingsLoader.getBoolean("linkTreeWithTab", false);
    if (GlobalSettings.linkTreeWithTab)
      rssOwlFavoritesTree.setLinkWithTabChecked(true);

    /** Mail subject and mail body */
    GlobalSettings.mailSubject = settingsLoader.getValue("mailSubject", null, "[TITLE]", false);
    GlobalSettings.mailBody = settingsLoader.getValue("mailBody", null, "[TITLE]\n[LINK]\n\n[DESCRIPTION]", false);

    /** Blogger path and arguments */
    GlobalSettings.bloggerPath = settingsLoader.getValue("bloggerPath", "value", "", false);
    GlobalSettings.bloggerArguments = settingsLoader.getValue("bloggerArguments", "value", "", false);

    /** Syntaxhighlight Color */
    GlobalSettings.syntaxHighlightColor = settingsLoader.loadColor("syntaxHighlightColor", new RGB(255, 255, 0));
    PaintShop.initSyntaxHighlightColor(display);

    /** Favorite Defaults */
    GlobalSettings.favDefaultReloadOnStartup = settingsLoader.getBoolean("favDefaultReloadOnStartup", false);
    GlobalSettings.favDefaultOpenOnStartup = settingsLoader.getBoolean("favDefaultOpenOnStartup", false);
    GlobalSettings.favDefaultAutoReloadInterval = Integer.parseInt(settingsLoader.getValue("favDefaultAutoReloadInterval", "value", "60"));

    /** Other settings */
    GlobalSettings.directOpenNews = settingsLoader.getBoolean("isDirectOpenNews", false);
    GlobalSettings.showChannelInfo = settingsLoader.getBoolean("showChannelInfo", true);
    GlobalSettings.directOpenEachNews = settingsLoader.getBoolean("directOpenEachNews", false);
    GlobalSettings.openNewBrowserWindow = settingsLoader.getBoolean("openNewBrowserWindow", true);
    GlobalSettings.checkUpdate = settingsLoader.getBoolean("checkUpdate", true);
    GlobalSettings.isDoubleClickOpen = settingsLoader.getBoolean("isDoubleClickOpen", !GlobalSettings.useSingleClickOpen());
    GlobalSettings.focusNewTabs = settingsLoader.getBoolean("focusNewTabs", true);
    GlobalSettings.htmlFormatMail = settingsLoader.getBoolean("htmlFormatMail", true);
    GlobalSettings.showErrors = settingsLoader.getBoolean("showErrors", true);
    GlobalSettings.reopenFeeds = settingsLoader.getBoolean("reopenFeeds", false);
    GlobalSettings.autoCloseNewsPopup = settingsLoader.getBoolean("autoCloseNewsPopup", true);
    GlobalSettings.animateNewsPopup = settingsLoader.getBoolean("animateNewsPopup", true);
    GlobalSettings.markAllReadOnMinimize = settingsLoader.getBoolean("markAllReadOnMinimize", false);
    GlobalSettings.markFeedReadOnTabClose = settingsLoader.getBoolean("markFeedReadOnTabClose", false);
    GlobalSettings.customBrowser = settingsLoader.getValue("customBrowser", "path");
    GlobalSettings.customBrowserArguments = settingsLoader.getValue("customBrowserArguments", "value");
    GlobalSettings.setProxyForAllFavorites = settingsLoader.getBoolean("setProxyForAllFavorites", true);

    /*
     * AmphetaRate GlobalSettings.amphetaRateUserID =
     * settingsLoader.getValue("amphetaRateUserId", "userId");
     * GlobalSettings.amphetaRateUsername =
     * settingsLoader.getValue("amphetaRateUsername", "username");
     * GlobalSettings.amphetaRatePassword =
     * settingsLoader.getValue("amphetaRatePassword", "password");
     */

    /** Only Show AmphetaRate ToolBar if username given */
    rssOwlNewsText.setNewsTextToolBarVisible(false /* StringShop.isset(GlobalSettings.amphetaRateUsername) */);
    rssOwlStatusLine.setAmphetaRateItemsVisible(false /* StringShop.isset(GlobalSettings.amphetaRateUsername) */);

    if (!GlobalSettings.isLinux())
      GlobalSettings.blockPopups = settingsLoader.getBoolean("blockPopups", true);

    if (GlobalSettings.useBrowserForNewsText && !isHotLoad)
      rssOwlMenu.setUseBrowserForNewsText(GlobalSettings.useBrowserForNewsText);

    /** AmphetaRate rating */
    // settingsLoader.loadRatings();
    
    /** Hotkeys */
    settingsLoader.loadHotKeys();
    rssOwlMenu.updateAccelerators();
    rssOwlMenu.initMnemonics();
    rssOwlQuickView.updateI18N();

    /** Tab Style */
    GlobalSettings.simpleTabs = settingsLoader.getBoolean("simpleTabs", true);
    rssOwlNewsTabFolder.getNewsHeaderTabFolder().setSimple(GlobalSettings.simpleTabs);
    rssOwlNewsTabFolder.getNewsHeaderTabFolder().setSelectionBackground((GlobalSettings.displaySingleTab) ? PaintShop.grayViewFormColor : display.getSystemColor(SWT.COLOR_WHITE));
    rssOwlNewsTabFolder.getNewsHeaderTabFolder().setTabHeight(GlobalSettings.displaySingleTab ? 0 : -1);

    /** Single Tab View looks better if single tab is bottom aligned */
    if (GlobalSettings.displaySingleTab)
      rssOwlGui.getRSSOwlNewsTabFolder().getNewsHeaderTabFolder().setTabPosition(SWT.BOTTOM);
    else
      rssOwlNewsTabFolder.getNewsHeaderTabFolder().setTabPosition((GlobalSettings.tabPositionIsTop == true) ? SWT.TOP : SWT.BOTTOM);

    /** Load the reopen feeds if set so */
    if (GlobalSettings.reopenFeeds)
      settingsLoader.loadReopenFeeds();

    /** Enable Systray Support if OS supports it and if not yet enabled */
    if (GlobalSettings.useSystemTray() && GlobalSettings.showSystrayIcon && rssOwlSystray == null)
      enableSystrayIcon(true);

    /** Disable Systray Support if enabled but no longer wanting */
    else if (!GlobalSettings.showSystrayIcon && rssOwlSystray != null)
      enableSystrayIcon(false);
  }
}