From b6afbe8c699ae76443628badae33beee9934c6bc Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Wed, 21 Sep 2016 21:29:39 +0200
Subject: Provided a real welcome panel at startup, with tricks and actions.

---
 ChangeLog                              |  68 +++
 configure.ac                           |   1 -
 pixmaps/tipoftheday.png                | Bin 0 -> 10206 bytes
 pixmaps/tipoftheday.xcf                | Bin 0 -> 17942 bytes
 plugins/python/Makefile.am             |   2 +-
 plugins/python/welcome/Makefile.am     |  14 -
 plugins/python/welcome/__init__.py     |   4 -
 plugins/python/welcome/binary.py       |  40 --
 plugins/python/welcome/board.py        | 102 ----
 plugins/python/welcome/panel.py        | 148 ------
 plugins/python/welcome/plugin.py       |  36 --
 plugins/python/welcome/tip.py          | 146 ------
 plugins/python/welcome/tipoftheday.png | Bin 10206 -> 0 bytes
 plugins/python/welcome/tipoftheday.xcf | Bin 17942 -> 0 bytes
 plugins/python/welcome/version.py      | 116 -----
 plugins/python/welcome/website.py      |  30 --
 src/common/Makefile.am                 |   1 +
 src/common/net.c                       |  57 +++
 src/common/net.h                       |   4 +
 src/common/shuffle.c                   |  78 +++
 src/common/shuffle.h                   |  37 ++
 src/core/params.c                      |   6 +
 src/core/params.h                      |   2 +
 src/gui/core/panels.c                  |   4 +
 src/gui/editor.c                       | 109 ++++-
 src/gui/panels/Makefile.am             |  19 +-
 src/gui/panels/gresource.xml           |   6 +
 src/gui/panels/panel-int.h             |  10 +-
 src/gui/panels/panel.c                 |   6 +
 src/gui/panels/regedit.c               |   2 +-
 src/gui/panels/welcome.c               | 865 +++++++++++++++++++++++++++++++++
 src/gui/panels/welcome.h               |  68 +++
 src/gui/panels/welcome.ui              | 245 ++++++++++
 src/main.c                             |  15 +-
 34 files changed, 1571 insertions(+), 670 deletions(-)
 create mode 100644 pixmaps/tipoftheday.png
 create mode 100644 pixmaps/tipoftheday.xcf
 delete mode 100644 plugins/python/welcome/Makefile.am
 delete mode 100644 plugins/python/welcome/__init__.py
 delete mode 100644 plugins/python/welcome/binary.py
 delete mode 100644 plugins/python/welcome/board.py
 delete mode 100644 plugins/python/welcome/panel.py
 delete mode 100644 plugins/python/welcome/plugin.py
 delete mode 100644 plugins/python/welcome/tip.py
 delete mode 100644 plugins/python/welcome/tipoftheday.png
 delete mode 100644 plugins/python/welcome/tipoftheday.xcf
 delete mode 100644 plugins/python/welcome/version.py
 delete mode 100644 plugins/python/welcome/website.py
 create mode 100644 src/common/shuffle.c
 create mode 100644 src/common/shuffle.h
 create mode 100644 src/gui/panels/gresource.xml
 create mode 100644 src/gui/panels/welcome.c
 create mode 100644 src/gui/panels/welcome.h
 create mode 100644 src/gui/panels/welcome.ui

diff --git a/ChangeLog b/ChangeLog
index 5bb07f9..5750fb2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,71 @@
+16-09-21  Cyrille Bagard <nocbos@gmail.com>
+
+	* configure.ac:
+	Remove the Makefile from the 'plugins/python/welcome' directory.
+
+	* pixmaps/tipoftheday.png:
+	* pixmaps/tipoftheday.xcf:
+	Moved entries from the 'plugins/python/welcome' directory.
+
+	* plugins/python/Makefile.am:
+	Remove 'welcome' from SUBDIRS.
+
+	* plugins/python/welcome/Makefile.am:
+	* plugins/python/welcome/__init__.py:
+	* plugins/python/welcome/binary.py:
+	* plugins/python/welcome/board.py:
+	* plugins/python/welcome/panel.py:
+	* plugins/python/welcome/plugin.py:
+	* plugins/python/welcome/tip.py:
+	* plugins/python/welcome/tipoftheday.png:
+	* plugins/python/welcome/tipoftheday.xcf:
+	* plugins/python/welcome/version.py:
+	* plugins/python/welcome/website.py:
+	Deleted entries.
+
+	* src/common/Makefile.am:
+	Add the 'shuffle.[ch]' files to libcommon_la_SOURCES.
+
+	* src/common/net.c:
+	* src/common/net.h:
+	Provide a convenient way to read data from sockets.
+
+	* src/common/shuffle.c:
+	* src/common/shuffle.h:
+	New entries: shuffle list of items using the Fisher-Yates algorithm.
+
+	* src/core/params.c:
+	* src/core/params.h:
+	Add some new parameters for the welcome panel.
+
+	* src/gui/core/panels.c:
+	Register the welcome panel.
+
+	* src/gui/editor.c:
+	Update code to show / hide the welcome panel when needed.
+
+	* src/gui/panels/Makefile.am:
+	Add the 'welcome.[ch]' files to libguipanels_la_SOURCES and instructions
+	to build resources from the 'welcome.ui' file.
+
+	* src/gui/panels/gresource.xml:
+	New entry: define how to build GUI resources.
+
+	* src/gui/panels/panel-int.h:
+	* src/gui/panels/panel.c:
+	Notify panels about docking / undocking processes.
+
+	* src/gui/panels/regedit.c:
+	Update code: do not show the configuration panel at startup.
+
+	* src/gui/panels/welcome.c:
+	* src/gui/panels/welcome.h:
+	* src/gui/panels/welcome.ui:
+	New entries: provide a real welcome panel at startup, with tricks and actions.
+
+	* src/main.c:
+	Update code.
+
 16-09-14  Cyrille Bagard <nocbos@gmail.com>
 
 	* src/arch/dalvik/opdefs/if_32.d:
diff --git a/configure.ac b/configure.ac
index d8d74df..947867d 100644
--- a/configure.ac
+++ b/configure.ac
@@ -325,7 +325,6 @@ AC_CONFIG_FILES([Makefile
                  plugins/python/apkfiles/Makefile
                  plugins/python/exectracer/Makefile
                  plugins/python/samples/Makefile
-                 plugins/python/welcome/Makefile
                  plugins/readdex/Makefile
                  plugins/readelf/Makefile
                  plugins/ropgadgets/Makefile
diff --git a/pixmaps/tipoftheday.png b/pixmaps/tipoftheday.png
new file mode 100644
index 0000000..12f017a
Binary files /dev/null and b/pixmaps/tipoftheday.png differ
diff --git a/pixmaps/tipoftheday.xcf b/pixmaps/tipoftheday.xcf
new file mode 100644
index 0000000..dc9cf06
Binary files /dev/null and b/pixmaps/tipoftheday.xcf differ
diff --git a/plugins/python/Makefile.am b/plugins/python/Makefile.am
index ae5baed..35f15eb 100644
--- a/plugins/python/Makefile.am
+++ b/plugins/python/Makefile.am
@@ -1,2 +1,2 @@
 
-SUBDIRS = apkfiles samples welcome
+SUBDIRS = apkfiles samples
diff --git a/plugins/python/welcome/Makefile.am b/plugins/python/welcome/Makefile.am
deleted file mode 100644
index 7dd16fa..0000000
--- a/plugins/python/welcome/Makefile.am
+++ /dev/null
@@ -1,14 +0,0 @@
-
-welcomedir = $(datadir)/openida/plugins/python/welcome
-
-welcome_DATA =								\
-	__init__.py								\
-	binary.py								\
-	board.py								\
-	panel.py								\
-	plugin.py								\
-	tip.py									\
-	version.py								\
-	website.py								\
-	tipoftheday.png							\
-	tipoftheday.xcf
diff --git a/plugins/python/welcome/__init__.py b/plugins/python/welcome/__init__.py
deleted file mode 100644
index 00a76af..0000000
--- a/plugins/python/welcome/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.plugin import WelcomePlugin as AutoLoad
diff --git a/plugins/python/welcome/binary.py b/plugins/python/welcome/binary.py
deleted file mode 100644
index 205934e..0000000
--- a/plugins/python/welcome/binary.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.board import SmallBoard
-from gi.repository import Gtk, Gdk
-import pychrysalide
-
-
-class NewBinary(SmallBoard):
-    """Encourage l'ouverture d'un (premier) binaire."""
-
-
-    def __init__(self):
-        """Construit le panneau avec son contenu."""
-
-        super(NewBinary, self).__init__()
-
-        self._menu_item = pychrysalide.get_global_gobject('mnu_project_add_binary')
-
-        msg = 'Analyse a new binary by clicking <a href="file:///#">here</a>.'
-
-        desc = Gtk.Label(msg, use_markup=True, wrap=True)
-        desc.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
-        desc.set_track_visited_links(False)
-        desc.connect('activate-link', self.on_link_activated)
-        self.add(desc)
-
-
-    def on_link_activated(self, uri, data):
-        """Lance l'action associée au lien représenté."""
-
-        self._menu_item.activate()
-
-        return True
-
-
-    def get_location(self):
-        """Fournit la localisation souhaitée pour la planche."""
-
-        return [ 0, 0, 1, 1 ]
diff --git a/plugins/python/welcome/board.py b/plugins/python/welcome/board.py
deleted file mode 100644
index 39cd885..0000000
--- a/plugins/python/welcome/board.py
+++ /dev/null
@@ -1,102 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from gi.repository import Gtk
-
-
-class SmallBoard(Gtk.EventBox):
-    """Représente une tuile de support pour le panneau de bienvenue."""
-
-
-    css = """
-
-#classic {
-
-    border-color: rgba(255, 255, 255, 0.2);
-    border-style: solid;
-    border-width: 1px;
-
-}
-
-#hover {
-
-    border-color: rgba(255, 255, 255, 0.6);
-    border-style: solid;
-    border-width: 1px;
-
-}
-"""
-
-
-    def __init__(self):
-        """Construit le panneau avec son contenu."""
-
-        super(SmallBoard, self).__init__()
-
-        #self.set_size_request(250, 150)
-        self.set_has_window(True)
-
-        self.props.opacity = 0.6
-
-        self._manager = None
-
-
-    def on_enter(self, widget, event):
-        """Réagit à un survol de la zone par la souris."""
-
-        self.set_name('hover')
-
-        self._manager.define_selected_area(self.get_allocation())
-
-        self.props.opacity = 1.0
-
-        return False
-
-
-    def on_leave(self, widget, event):
-        """Réagit à une sortie de la zone par la souris."""
-
-        self.set_name('classic')
-
-        if self._manager != None:
-            self._manager.define_selected_area(None)
-
-        self.props.opacity = 0.6
-
-        return False
-
-
-    def get_location(self):
-        """Fournit la localisation souhaitée pour la planche."""
-
-        pass
-
-
-    def attach(self, manager):
-        """Lie partiellement la planche à son support et suit les survols."""
-
-        child = self.get_child()
-
-        if child != None:
-
-            child.props.margin = 20
-
-            self.set_name('classic')
-
-            self._manager = manager
-
-
-            def track_children(widget, owner):
-
-                widget.connect('enter-notify-event', self.on_enter)
-                widget.connect('leave-notify-event', self.on_leave)
-
-                if isinstance(widget, Gtk.Container):
-
-                    children = widget.get_children()
-
-                    for child in children:
-                        track_children(child, owner)
-
-
-            track_children(self, self)
diff --git a/plugins/python/welcome/panel.py b/plugins/python/welcome/panel.py
deleted file mode 100644
index a577c8c..0000000
--- a/plugins/python/welcome/panel.py
+++ /dev/null
@@ -1,148 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.binary import NewBinary
-from welcome.version import VersionChecker
-from welcome.website import WebInvitation
-from welcome.board import SmallBoard
-from welcome.tip import TipOfTheDay
-from gi.repository import GObject, Gtk, Gdk
-from pychrysalide.gui.panels import PanelItem
-
-
-class WelcomePanel(PanelItem):
-    """Display a welcome panel if nothing is open in the main part of the editor."""
-
-
-    def __init__(self):
-        """Initialize the Python instance of the panel."""
-
-        content = self._build_panel_content()
-
-        super(WelcomePanel, self).__init__(PanelItem.PIP_SINGLETON, 'Welcome', 'First commands', \
-                                           content, False, 'N')
-
-
-    def _build_panel_content(self):
-        """Build content for the welcome panel."""
-
-        self._area = None
-
-        # Constitution du support principal
-
-        support = Gtk.Grid()
-
-        support.props.halign = Gtk.Align.CENTER
-        support.props.valign = Gtk.Align.CENTER
-        support.props.margin = 20
-
-        support.set_column_homogeneous(True)
-        support.set_row_homogeneous(True)
-        support.set_column_spacing(20)
-        support.set_row_spacing(20)
-
-        # Mise en place des différentes tuiles
-
-        cells = { }
-
-        for x in range(4):
-            for y in range(3):
-                cells[(x, y)] = False
-
-        tiles = [
-
-            NewBinary(),
-            VersionChecker(),
-            WebInvitation(),
-            TipOfTheDay()
-
-        ]
-
-        for t in tiles:
-
-            x, y, w, h = t.get_location()
-
-            for i in range(x, x + w):
-                for j in range(y, y + h):
-                    assert(cells[(i, j)] == False)
-                    cells[(i, j)] = True
-
-            t.attach(self)
-            support.attach(t, x, y, w, h)
-
-        for x in range(4):
-            for y in range(3):
-                if not cells[(x, y)]:
-                    tile = SmallBoard()
-                    tile.attach(self)
-                    support.attach(tile, x, y, 1, 1)
-
-        # Charge les styles propres aux panneaux
-
-        style_provider = Gtk.CssProvider()
-
-        style_provider.load_from_data(SmallBoard.css.encode())
-
-        Gtk.StyleContext.add_provider_for_screen(
-            Gdk.Screen.get_default(), 
-            style_provider,     
-            Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
-        )
-
-
-        support.show_all()
-        return support
-
-
-    def define_selected_area(self, area):
-
-        self._area = area
-
-        ####
-        #self.queue_draw()
-
-
-    def draw_selected_area(self, widget, cr):
-
-
-        alloc = widget.get_allocation()
-
-
-
-
-        def draw_rounded2(cr, area, radius):
-            """ draws rectangles with rounded (circular arc) corners """
-            from math import pi
-            a,b,c,d=area
-            cr.arc(a + radius, c + radius, radius, 2*(pi/2), 3*(pi/2))
-            cr.arc(b - radius, c + radius, radius, 3*(pi/2), 4*(pi/2))
-            cr.arc(b - radius, d - radius, radius, 0*(pi/2), 1*(pi/2))  # ;o)
-            cr.arc(a + radius, d - radius, radius, 1*(pi/2), 2*(pi/2))
-            cr.rectangle(self._area.x, self._area.y, self._area.width, self._area.height)
-            cr.close_path()
-            cr.fill()
-
-
-        if self._area != None:
-            cr.set_source_rgba(0, 0, 0, 0.15)
-
-            border = 4
-
-            pts = ( self._area.x - border, self._area.x + self._area.width + border,
-                    self._area.y - border, self._area.y + self._area.height + border )
-
-            #cr.save()
-
-            draw_rounded2(cr, pts, 16)
-
-            ctx = self.get_style_context()
-
-
-            Gtk.render_background(ctx, cr, 0, 20, 100, 100)
-            Gtk.render_background(ctx, cr, self._area.x, self._area.y, self._area.width, self._area.height)
-
-
-            #cr.restore()
-
-
-        return False
diff --git a/plugins/python/welcome/plugin.py b/plugins/python/welcome/plugin.py
deleted file mode 100644
index a26c93b..0000000
--- a/plugins/python/welcome/plugin.py
+++ /dev/null
@@ -1,36 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from pychrysalide import PluginModule
-from welcome.panel import WelcomePanel
-
-
-class WelcomePlugin(PluginModule):
-    """Interface graphique d'accueil."""
-
-
-    def get_interface(self):
-        """Provide the full plugin description."""
-
-        desc = {
-
-            'name' : 'Welcome',
-            'desc' : 'Introduce the software when no project is loaded',
-            'version' : '0.1',
-
-            'actions' : [ PluginModule.PGA_PLUGIN_INIT ]
-
-        }
-
-        return desc
-
-
-    def init(self, ref):
-        """Initialise l'extension."""
-
-        self._panel = WelcomePanel()
-
-        #self._panel.dock()
-        #self._panel.register()
-
-        return True
diff --git a/plugins/python/welcome/tip.py b/plugins/python/welcome/tip.py
deleted file mode 100644
index 642619e..0000000
--- a/plugins/python/welcome/tip.py
+++ /dev/null
@@ -1,146 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.board import SmallBoard
-from gi.repository import Gtk
-import random
-
-try:
-    import cairo
-    has_cairo = True
-    import os
-except:
-    has_cairo = False
-
-
-_tip_messages = [
-    "Message 1",
-    "Message 2",
-    "Message 3",
-    "Message 4",
-    "Message 5",
-    "Message 6"
-]
-
-assert(len(_tip_messages) > 0)
-
-
-class TipOfTheDay(SmallBoard):
-    """Présente une série d'astuces du jour."""
-
-
-    def __init__(self):
-        """Construit le panneau avec son contenu."""
-
-        super(TipOfTheDay, self).__init__()
-
-        box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=10)
-        self.add(box)
-
-        # Barre de contrôle
-
-        toolbar = Gtk.Box(spacing=10)
-        toolbar.props.margin = 20
-        box.pack_end(toolbar, False, True, 0)
-
-        btn = Gtk.Button('Previous')
-        btn.connect('clicked', self.show_previous_message)
-        toolbar.pack_start(btn, False, True, 8)
-
-        btn = Gtk.Button('Next')
-        btn.connect('clicked', self.show_next_message)
-        toolbar.pack_start(btn, False, True, 8)
-
-        # Contenu de l'astuce courante
-
-        desc = Gtk.Label('', use_markup=True, wrap=True, xalign=0)
-        self._message = desc
-        desc.set_justify(Gtk.Justification.LEFT)
-        box.pack_end(desc, False, True, 0)
-
-        # Titre
-
-        desc = Gtk.Label("<b>Did you know?</b>", use_markup=True, wrap=True, xalign=0)
-        desc.set_justify(Gtk.Justification.LEFT)
-        box.pack_end(desc, False, True, 0)
-
-        # Image de fond, si possible
-
-        if has_cairo:
-
-            os.chdir(os.path.dirname(__file__))
-
-            self._img = cairo.ImageSurface.create_from_png('tipoftheday.png')
-
-            self.connect_after('draw', self.draw_background)
-
-        # Sélection du message courant
-
-        self._indexes = [i for i in range(len(_tip_messages))]
-        random.shuffle(self._indexes)
-
-        self._index = 0
-
-        self.show_message()
-
-
-    def draw_background(self, widget, cr):
-        """Dessine une image de fond pour les conseils."""
-
-        img_width = self._img.get_width()
-        img_height = self._img.get_height()
-
-        alloc = self.get_allocation()
-        target_height = alloc.height * 0.7
-
-        if img_height > target_height:
-
-            scale = float(target_height) / float(img_height)
-
-            cr.translate(alloc.width - img_width * scale - 10, 10)
-            cr.scale(scale, scale)
-
-        else:
-
-            cr.translate(alloc.width - img_width - 10, 10)
-
-        cr.set_source_surface(self._img)
-        cr.paint_with_alpha(0.6)
-
-
-    def show_message(self):
-        """Affiche un conseil du jour donné."""
-
-        selected = self._indexes[self._index]
-
-        msg = _tip_messages[selected]
-
-        self._message.set_markup(msg)
-
-
-    def show_previous_message(self, button):
-        """Affiche un conseil du jour précédent."""
-
-        if self._index == 0:
-            self._index = len(_tip_messages) - 1
-        else:
-            self._index = self._index - 1
-
-        self.show_message()
-
-
-    def show_next_message(self, button):
-        """Affiche un conseil du jour suivant."""
-
-        self._index = self._index + 1
-
-        if self._index == len(_tip_messages):
-            self._index = 0
-
-        self.show_message()
-
-
-    def get_location(self):
-        """Fournit la localisation souhaitée pour la planche."""
-
-        return [ 2, 1, 2, 2 ]
diff --git a/plugins/python/welcome/tipoftheday.png b/plugins/python/welcome/tipoftheday.png
deleted file mode 100644
index 12f017a..0000000
Binary files a/plugins/python/welcome/tipoftheday.png and /dev/null differ
diff --git a/plugins/python/welcome/tipoftheday.xcf b/plugins/python/welcome/tipoftheday.xcf
deleted file mode 100644
index dc9cf06..0000000
Binary files a/plugins/python/welcome/tipoftheday.xcf and /dev/null differ
diff --git a/plugins/python/welcome/version.py b/plugins/python/welcome/version.py
deleted file mode 100644
index 9bd4db2..0000000
--- a/plugins/python/welcome/version.py
+++ /dev/null
@@ -1,116 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.board import SmallBoard
-from gi.repository import Gtk
-import pychrysalide
-
-try:
-    import urllib3
-    has_urllib3 = True
-except:
-    has_urllib3 = False
-
-
-class VersionChecker(SmallBoard):
-    """Affichage des versions courante et disponible, ainsi que des conclusions associées."""
-
-
-    def __init__(self):
-        """Construit le panneau avec son contenu."""
-
-        super(VersionChecker, self).__init__()
-
-        current = pychrysalide.revision()
-
-        if has_urllib3:
-
-            lastest = self.get_lastest_version('community')            
-
-            if lastest != None:
-                up2date = self.get_update_status(current, lastest)
-            else:
-                up2date = None
-
-            if lastest != None and up2date != None:
-
-                msg = '''Your version is: <b>%s</b>
-
-Lastest version is: <b>%s</b>'''
-
-                caption = msg % (current, lastest if lastest != None else 'unknown')
-
-                if up2date:
-                    caption = caption + '''
-
-Your software is <span color='green'><b>up-to-date</b></span>.'''
-                else:
-                    caption = caption + '''
-
-Your software is <b><span color='red'>outdated</span></b>.'''
-
-            else:
-
-                msg = '''Your version is: <b>%s</b>
-            
-An error occurred while dealing with revision numbers.'''
-
-                caption = msg % current
-
-        else:
-
-            msg = '''Your version is: <b>%s</b>
-
-To display the lastest available version, please install the <b>urllib3</b> package for Python.'''
-
-            caption = msg % current
-
-        desc = Gtk.Label(caption, use_markup=True, wrap=True)
-        desc.set_track_visited_links(False)
-        self.add(desc)
-
-
-    def get_lastest_version(self, category):
-        """Retrouve la dernière version disponible à partir du site officiel."""
-
-        lastest = None
-
-        http = urllib3.PoolManager()
-        try:
-            request = http.request('GET', 'http://0xdeadc0de.fr/chrysalide/updates/versions')
-        except urllib3.exceptions.MaxRetryError:
-            request = None
-
-        if request != None:
-
-            html = request.data.decode('utf-8')
-
-            request.release_conn()
-
-            available = html.split('\n')
-
-            for a in available:
-                desc = a.split('=')
-                if desc[0] == category:
-                    lastest = desc[1]
-                    break
-
-        return lastest
-
-
-    def get_update_status(self, current, lastest):
-        """Détermine le degré de mise à jour du système."""
-
-        if current[0] != 'r' or lastest[0] != 'r':
-            return None
-
-        cur_rev = int(current[1:])
-        last_rev = int(lastest[1:])
-
-        return (cur_rev >= last_rev)
-
-
-    def get_location(self):
-        """Fournit la localisation souhaitée pour la planche."""
-
-        return [ 2, 0, 1, 1 ]
diff --git a/plugins/python/welcome/website.py b/plugins/python/welcome/website.py
deleted file mode 100644
index f8f1b9a..0000000
--- a/plugins/python/welcome/website.py
+++ /dev/null
@@ -1,30 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-from welcome.board import SmallBoard
-from gi.repository import Gtk, Gdk
-
-
-class WebInvitation(SmallBoard):
-    """Présente un lien rapide vers le site Web officiel."""
-
-
-    def __init__(self):
-        """Construit le panneau avec son contenu."""
-
-        super(WebInvitation, self).__init__()
-
-        msg = '''Get access to the online documentation and stay tuned by visiting the official website :
-
-<a href="http://0xdeadc0de.fr/chrysalide/">0xdeadc0de.fr/chrysalide</a>'''
-
-        desc = Gtk.Label(msg, use_markup=True, wrap=True)
-        desc.add_events(Gdk.EventMask.ENTER_NOTIFY_MASK)
-        desc.set_track_visited_links(False)
-        self.add(desc)
-
-
-    def get_location(self):
-        """Fournit la localisation souhaitée pour la planche."""
-
-        return [ 3, 0, 1, 1 ]
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 9e9969d..02c1718 100755
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -16,6 +16,7 @@ libcommon_la_SOURCES =					\
 	macros.h							\
 	net.h net.c							\
 	pathname.h pathname.c				\
+	shuffle.h shuffle.c					\
 	sort.h sort.c						\
 	sqlite.h sqlite.c					\
 	xdg.h xdg.c							\
diff --git a/src/common/net.c b/src/common/net.c
index aba5ba2..b741883 100644
--- a/src/common/net.c
+++ b/src/common/net.c
@@ -24,6 +24,7 @@
 #include "net.h"
 
 
+#include <errno.h>
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -100,3 +101,59 @@ int connect_via_tcp(const char *server, const char *port, struct sockaddr_in *ad
     return result;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : sock   = nom ou adresse du serveur à contacter.              *
+*                buffer = tampon pour la réception des données. [OUT]         *
+*                max    = taille prévue pour ce tampon.                       *
+*                len    = quantité de données effectivement reçues. [OUT]     *
+*                                                                             *
+*  Description : Reçoit du réseau autant de données que possible.             *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool recv_all(int sock, char *buffer, size_t max, size_t *len)
+{
+    bool result;                            /* Bilan global à retourner    */
+    ssize_t ret;                            /* Bilan d'un appel            */
+
+    result = true;
+
+    buffer[0] = '\0';
+    max--;  /* '\0' final */
+    *len = 0;
+
+    do
+    {
+        ret = recv(sock, buffer + *len, max, *len > 0 ? MSG_DONTWAIT : 0);
+
+        printf("ret = %zd\n", ret);
+
+        if (ret == -1 && *len == 0)
+        {
+            if (errno == EAGAIN || errno == EWOULDBLOCK)
+                continue;
+            else
+                result = false;
+        }
+
+        else if (ret > 0)
+        {
+            max -= ret;
+            *len += ret;
+        }
+
+    }
+    while (ret > 0);
+
+    buffer[*len] = '\0';
+
+    return result;
+
+}
diff --git a/src/common/net.h b/src/common/net.h
index 80f7496..d9ec6f9 100644
--- a/src/common/net.h
+++ b/src/common/net.h
@@ -26,12 +26,16 @@
 
 
 #include <netdb.h>
+#include <stdbool.h>
 
 
 
 /* Ouvre une connexion TCP à un serveur quelconque. */
 int connect_via_tcp(const char *, const char *, struct sockaddr_in *);
 
+/* Reçoit du réseau autant de données que possible. */
+bool recv_all(int, char *, size_t, size_t *);
+
 
 
 #endif  /* _COMMON_NET_H */
diff --git a/src/common/shuffle.c b/src/common/shuffle.c
new file mode 100644
index 0000000..6b20866
--- /dev/null
+++ b/src/common/shuffle.c
@@ -0,0 +1,78 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * shuffle.c - permtutation aléatoire des éléments d'un ensemble fini
+ *
+ * Copyright (C) 2016 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  OpenIDA is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#include "shuffle.h"
+
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Mélange le contenu d'une liste.                              *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void shuffle(void *base, size_t nmemb, size_t size)
+{
+    char *list;                             /* Conversion pour le confort  */
+    char *tmp;                              /* Lieu de transition          */
+    size_t i;                               /* Boucle de parcours          */
+    size_t j;                               /* Emplacement aléatoire       */
+
+    /**
+     * Application de l'algorithme Fisher-Yates.
+     *
+     * Cf. https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle
+     */
+
+    if (nmemb > 1)
+    {
+        list = (char *)base;
+
+        tmp = malloc(size);
+
+        for (i = 0; i < (nmemb - 1); i++)
+        {
+            j = i + rand() % (nmemb - i);
+
+            memcpy(tmp, list + i * size, size);
+            memcpy(list + i * size, list + j * size, size);
+            memcpy(list + j * size, tmp, size);
+
+        }
+
+        free(tmp);
+
+    }
+
+}
diff --git a/src/common/shuffle.h b/src/common/shuffle.h
new file mode 100644
index 0000000..c1a0367
--- /dev/null
+++ b/src/common/shuffle.h
@@ -0,0 +1,37 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * shuffle.h - prototypes pour la permtutation aléatoire des éléments d'un ensemble fini
+ *
+ * Copyright (C) 2016 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  OpenIDA is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
+#ifndef _COMMON_SHUFFLE_H
+#define _COMMON_SHUFFLE_H
+
+
+#include <sys/types.h>
+
+
+
+/* Mélange le contenu d'une liste. */
+void shuffle(void *, size_t, size_t);
+
+
+
+#endif  /* _COMMON_SHUFFLE_H */
diff --git a/src/core/params.c b/src/core/params.c
index 52dae76..08b9604 100644
--- a/src/core/params.c
+++ b/src/core/params.c
@@ -156,6 +156,12 @@ bool load_main_config_parameters(void)
     param = g_generic_config_create_param(config, MPK_DISPLAY_ON_SEL, CPT_BOOLEAN, false);
     if (param == NULL) return false;
 
+    param = g_generic_config_create_param(config, MPK_WELCOME_STARTUP, CPT_BOOLEAN, true);
+    if (param == NULL) return false;
+
+    param = g_generic_config_create_param(config, MPK_WELCOME_CHECK, CPT_BOOLEAN, false);
+    if (param == NULL) return false;
+
     param = g_generic_config_create_param(config, MPK_SELECTION_LINE, CPT_BOOLEAN, true);
     if (param == NULL) return false;
 
diff --git a/src/core/params.h b/src/core/params.h
index 53f7483..7210ad0 100644
--- a/src/core/params.h
+++ b/src/core/params.h
@@ -46,6 +46,8 @@
 #define MPK_ELLIPSIS_HEADER     "gui.editor.panels.ellipsis_header"
 #define MPK_ELLIPSIS_TAB        "gui.editor.panels.ellipsis_tab"
 #define MPK_DISPLAY_ON_SEL      "gui.editor.panels.display_on_selection"
+#define MPK_WELCOME_STARTUP     "gui.editor.panels.welcome.show_at_startup"
+#define MPK_WELCOME_CHECK       "gui.editor.panels.welcome.check_version"
 #define MPK_SELECTION_LINE      "gui.editor.views.selection_line"
 #define MPK_TOOLTIP_SIZE        "gui.editor.views.tooltip_max_size"
 #define MPK_KEYBINDINGS_EDIT    "gui.key_bindings.global.edit"
diff --git a/src/gui/core/panels.c b/src/gui/core/panels.c
index d8565f4..9dcb199 100644
--- a/src/gui/core/panels.c
+++ b/src/gui/core/panels.c
@@ -33,6 +33,7 @@
 #include "../panels/regedit.h"
 #include "../panels/strings.h"
 #include "../panels/symbols.h"
+#include "../panels/welcome.h"
 #include "../../core/params.h"
 #include "../../gtkext/gtkdockable.h"
 
@@ -62,6 +63,9 @@ void load_main_panels(GObject *ref)
 
     config = get_main_configuration();
 
+    item = g_welcome_panel_new();
+    register_panel_item(item, ref, config);
+
     item = g_log_panel_new();
     register_panel_item(item, ref, config);
 
diff --git a/src/gui/editor.c b/src/gui/editor.c
index 926201a..3723bd8 100644
--- a/src/gui/editor.c
+++ b/src/gui/editor.c
@@ -39,6 +39,7 @@
 #include "core/core.h"
 #include "core/panels.h"
 #include "panels/panel.h"
+#include "panels/welcome.h"
 #include "tb/portions.h"
 #include "tb/source.h"
 #include "../analysis/project.h"
@@ -158,7 +159,7 @@ static void update_path_of_paned_nodes(panel_node *, const char *, const char *)
 static size_t compute_path_common_length(const panel_node *, const char *);
 
 /* Place au bon endroit un panneau donné. */
-static void insert_item_as_panel_node(GPanelItem *, panel_node *, const char *, size_t);
+static panel_node *insert_item_as_panel_node(GPanelItem *, panel_node *, const char *, size_t);
 
 /* Tente de mettre la main sur une station d'accueil. */
 static panel_node *find_node_for_station(panel_node *, GtkWidget *);
@@ -788,18 +789,18 @@ static size_t compute_path_common_length(const panel_node *node, const char *tar
 *                                                                             *
 *  Description : Place au bon endroit un panneau donné.                       *
 *                                                                             *
-*  Retour      : -                                                            *
+*  Retour      : Noeud final inséré dans la liste.                            *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const char *path, size_t consumed)
+static panel_node *insert_item_as_panel_node(GPanelItem *item, panel_node *node, const char *path, size_t consumed)
 {
+    panel_node *result;                     /* Noeud ultime à renvoyer     */
     char div;                               /* Division demandée           */
     bool horiz;                             /* Traduction en composant     */
     bool first;                             /* Point d'insertion           */
-    panel_node *new;                        /* Nouveau noeud créé          */
     size_t common1;                         /* Tron common avec le côté #1 */
     size_t common2;                         /* Tron common avec le côté #2 */
 
@@ -813,7 +814,10 @@ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const
     {
         /* Le parcours s'arrête ici ! */
         if (strcmp(node->path, path) == 0)
+        {
             gtk_dock_station_add_dockable(GTK_DOCK_STATION(node->station), GTK_DOCKABLE(item));
+            result = node;
+        }
 
         /* On ne peut aller plus loin, on doit diviser... */
         else
@@ -828,9 +832,9 @@ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const
 
             switch_panel_node_into_paned(node, horiz, !first);
 
-            new = create_simple_panel_node_for_item(item, path);
+            result = create_simple_panel_node_for_item(item, path);
 
-            attach_panel_node_to_paned(node, new, first);
+            attach_panel_node_to_paned(node, result, first);
 
 
             fprintf(stderr, "1# [%p] widget = %p --- split :: %p // %p\n",
@@ -857,10 +861,10 @@ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const
         if (common1 > 0 || common2 > 0)
         {
             if (common1 > common2)
-                insert_item_as_panel_node(item, node->first, path, common1);
+                result = insert_item_as_panel_node(item, node->first, path, common1);
 
             else
-                insert_item_as_panel_node(item, node->second, path, common2);
+                result = insert_item_as_panel_node(item, node->second, path, common2);
 
         }
 
@@ -875,9 +879,9 @@ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const
 
             switch_panel_node_into_paned(node, horiz, !first);
 
-            new = create_simple_panel_node_for_item(item, path);
+            result = create_simple_panel_node_for_item(item, path);
 
-            attach_panel_node_to_paned(node, new, first);
+            attach_panel_node_to_paned(node, result, first);
 
 
                 fprintf(stderr, "2# [%p] split :: %p-%p // %p-%p\n", node,
@@ -888,6 +892,8 @@ static void insert_item_as_panel_node(GPanelItem *item, panel_node *node, const
 
     }
 
+    return result;
+
 }
 
 
@@ -1047,7 +1053,7 @@ static void delete_panel_node(panel_node *node)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = composant à retirer de l'affichage.                 *
+*  Paramètres  : panel  = composant à placer dans l'affichage.                *
 *                unused = adresse non utilisée ici.                           *
 *                                                                             *
 *  Description : Réagit à une demande de placement d'un panneau d'affichage.  *
@@ -1058,28 +1064,53 @@ static void delete_panel_node(panel_node *node)
 *                                                                             *
 ******************************************************************************/
 
-void on_panel_item_dock_request(GPanelItem *item, void *unused)
+void on_panel_item_dock_request(GPanelItem *panel, void *unused)
 {
     const char *path;                       /* Chemin d'accès              */
+    panel_node *node;                       /* Noeud à supprimer           */
+    const char *name;                       /* Nom du dernier panneau      */
+    GPanelItem *welcome;                    /* Panneau d'accueil           */
 
-    path = gtk_panel_item_get_path(item);
+    path = gtk_panel_item_get_path(panel);
 
     /* Tout est à faire... */
     if (_nodes == NULL)
     {
-        _nodes = create_simple_panel_node_for_item(item, path);
+        _nodes = create_simple_panel_node_for_item(panel, path);
         gtk_container_add(GTK_CONTAINER(_support), _nodes->widget);
     }
-    else insert_item_as_panel_node(item, _nodes, path, 0);
+    else
+    {
+        node = insert_item_as_panel_node(panel, _nodes, path, 0);
+
+        if (strncmp(node->path, "N", 1) == 0)
+        {
+            name = g_editor_item_get_name(G_EDITOR_ITEM(panel));
+
+            if (strcmp(name, PANEL_WELCOME_ID) != 0)
+            {
+                welcome = get_panel_item_by_name(PANEL_WELCOME_ID);
+
+                if (!g_welcome_panel_get_user_origin(G_WELCOME_PANEL(welcome)))
+                {
+                    /* Equivalent d'un appel "g_panel_item_undock(welcome)", avec effet immédiat */
+                    on_panel_item_undock_request(welcome, NULL);
+                }
+
+            }
+
+        }
+
+    }
 
-    g_panel_item_set_dock_status(item, true);
+    g_panel_item_set_dock_status(panel, true);
 
 }
 
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : item   = composant à retirer de l'affichage.                 *
+*  Paramètres  : panel  = composant à retirer de l'affichage.                 *
 *                unused = adresse non utilisée ici.                           *
 *                                                                             *
 *  Description : Réagit à une demande de suppression d'un panneau d'affichage.*
@@ -1090,25 +1121,53 @@ void on_panel_item_dock_request(GPanelItem *item, void *unused)
 *                                                                             *
 ******************************************************************************/
 
-void on_panel_item_undock_request(GPanelItem *item, void *unused)
+void on_panel_item_undock_request(GPanelItem *panel, void *unused)
 {
     GtkWidget *station;                     /* Support courant             */
+    GtkNotebook *notebook;                  /* Version parente de station  */
     panel_node *node;                       /* Noeud à supprimer           */
+    bool allowed;                           /* Fermture permise ?          */
+    const char *name;                       /* Nom du dernier panneau      */
+    GPanelItem *welcome;                    /* Panneau d'accueil           */
+
+    gtk_dockable_decompose(GTK_DOCKABLE(panel), &station);
+
+    notebook = GTK_NOTEBOOK(station);
 
-    gtk_dockable_decompose(GTK_DOCKABLE(item), &station);
+    node = find_node_for_station(_nodes, station);
+    assert(node != NULL);
 
-    gtk_dock_station_remove_dockable(GTK_DOCK_STATION(station), GTK_DOCKABLE(item));
+    allowed = true;
 
-    if (gtk_notebook_get_n_pages(GTK_NOTEBOOK(station)) == 0)
+    if (strcmp(node->path, "N") == 0 && gtk_notebook_get_n_pages(notebook) == 1)
     {
-        node = find_node_for_station(_nodes, station);
-        assert(node != NULL);
+        name = g_editor_item_get_name(G_EDITOR_ITEM(panel));
+
+        if (strcmp(name, PANEL_WELCOME_ID) == 0)
+            allowed = false;
+
+        else
+        {
+            welcome = get_panel_item_by_name(PANEL_WELCOME_ID);
+            g_welcome_panel_set_user_origin(G_WELCOME_PANEL(welcome), false);
+
+            /* Equivalent d'un appel "g_panel_item_dock(welcome)", avec effet immédiat */
+            on_panel_item_dock_request(welcome, NULL);
 
-        delete_panel_node(node);
+        }
 
     }
 
-    g_panel_item_set_dock_status(item, false);
+    if (allowed)
+    {
+        gtk_dock_station_remove_dockable(GTK_DOCK_STATION(station), GTK_DOCKABLE(panel));
+
+        if (gtk_notebook_get_n_pages(notebook) == 0)
+            delete_panel_node(node);
+
+        g_panel_item_set_dock_status(panel, false);
+
+    }
 
 }
 
diff --git a/src/gui/panels/Makefile.am b/src/gui/panels/Makefile.am
index 4746210..929aef1 100644
--- a/src/gui/panels/Makefile.am
+++ b/src/gui/panels/Makefile.am
@@ -1,6 +1,11 @@
 
+BUILT_SOURCES = resources.h resources.c
+
 noinst_LTLIBRARIES  = libguipanels.la
 
+UI_FILES =								\
+	welcome.ui
+
 libguipanels_la_SOURCES =				\
 	bookmarks.h bookmarks.c				\
 	glance.h glance.c					\
@@ -8,8 +13,10 @@ libguipanels_la_SOURCES =				\
 	log.h log.c							\
 	panel.h panel.c						\
 	regedit.h regedit.c					\
+	resources.h resources.c				\
 	strings.h strings.c					\
-	symbols.h symbols.c
+	symbols.h symbols.c					\
+	welcome.h welcome.c
 
 libguipanels_la_LDFLAGS = 
 
@@ -19,3 +26,13 @@ AM_CPPFLAGS = $(LIBGTK_CFLAGS) $(LIBXML_CFLAGS)
 AM_CFLAGS = $(DEBUG_CFLAGS) $(WARNING_FLAGS) $(COMPLIANCE_FLAGS)
 
 SUBDIRS = 
+
+
+resources.c: gresource.xml $(UI_FILES)
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-source --c-name gui_panels gresource.xml
+
+resources.h: gresource.xml
+	glib-compile-resources --target=$@ --sourcedir=$(srcdir) --generate-header --c-name gui_panels gresource.xml
+
+
+CLEANFILES = resources.h resources.c
diff --git a/src/gui/panels/gresource.xml b/src/gui/panels/gresource.xml
new file mode 100644
index 0000000..58d47e2
--- /dev/null
+++ b/src/gui/panels/gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+    <gresource prefix="/org/chrysalide/gui/panels">
+        <file compressed="true">welcome.ui</file>
+    </gresource>
+</gresources>
diff --git a/src/gui/panels/panel-int.h b/src/gui/panels/panel-int.h
index 7e44275..4b0ef0c 100644
--- a/src/gui/panels/panel-int.h
+++ b/src/gui/panels/panel-int.h
@@ -37,6 +37,13 @@
 
 
 
+/* Place un panneau dans l'ensemble affiché. */
+typedef void (* ack_dock_process_fc) (GPanelItem *item);
+
+/* Supprime un panneau de l'ensemble affiché. */
+typedef void (* ack_undock_process_fc) (GPanelItem *item);
+
+
 /* Elément réactif pour panneaux de l'éditeur (instance) */
 struct _GPanelItem
 {
@@ -63,7 +70,8 @@ struct _GPanelItemClass
     bool unique;                            /* Panneau instanciable ?      */
     const char *bindings;                   /* Raccourci clavier éventuel  */
 
-    //GtkBin *first;                          /* Elément racine              */
+    ack_dock_process_fc ack_dock;           /* Prise en compte d'accroche  */
+    ack_undock_process_fc ack_undock;       /* Prise en compte de décroche */
 
     /* Signaux */
 
diff --git a/src/gui/panels/panel.c b/src/gui/panels/panel.c
index ee0c102..edf57d3 100644
--- a/src/gui/panels/panel.c
+++ b/src/gui/panels/panel.c
@@ -474,6 +474,9 @@ void g_panel_item_dock(GPanelItem *item)
 
     g_signal_emit_by_name(item, "dock-request");
 
+    if (G_PANEL_ITEM_GET_CLASS(item)->ack_dock != NULL)
+        G_PANEL_ITEM_GET_CLASS(item)->ack_dock(item);
+
 }
 
 
@@ -543,4 +546,7 @@ void g_panel_item_undock(GPanelItem *item)
 
     g_signal_emit_by_name(item, "undock-request");
 
+    if (G_PANEL_ITEM_GET_CLASS(item)->ack_undock != NULL)
+        G_PANEL_ITEM_GET_CLASS(item)->ack_undock(item);
+
 }
diff --git a/src/gui/panels/regedit.c b/src/gui/panels/regedit.c
index 5e224ec..66f0330 100644
--- a/src/gui/panels/regedit.c
+++ b/src/gui/panels/regedit.c
@@ -225,7 +225,7 @@ static void g_regedit_panel_init(GRegeditPanel *panel)
 
     pitem->personality = PIP_SINGLETON;
     pitem->lname = _("Configuration parameters");
-    pitem->dock_at_startup = true;
+    pitem->dock_at_startup = false;
     pitem->path = strdup("N");
 
     /* Représentation graphique */
diff --git a/src/gui/panels/welcome.c b/src/gui/panels/welcome.c
new file mode 100644
index 0000000..209419f
--- /dev/null
+++ b/src/gui/panels/welcome.c
@@ -0,0 +1,865 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * welcome.c - panneau d'accueil par défaut
+ *
+ * Copyright (C) 2016 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  OpenIDA is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "welcome.h"
+
+
+#include <assert.h>
+#include <malloc.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+#include <config.h>
+#include <i18n.h>
+
+
+#include "panel-int.h"
+#include "../../common/cpp.h"
+#include "../../common/io.h"
+#include "../../common/net.h"
+#include "../../common/shuffle.h"
+#include "../../core/params.h"
+#include "../../gtkext/support.h"
+
+
+
+/* Panneau d'accueil par défaut (instance) */
+struct _GWelcomePanel
+{
+    GPanelItem parent;                      /* A laisser en premier        */
+
+    GtkBuilder *builder;                    /* Constructeur utilisé        */
+    cairo_surface_t *background;            /* Fond pour astuces           */
+
+    char **tips;                            /* Liste de toutes les astuces */
+    size_t count;                           /* Quantité d'astuces          */
+    size_t current;                         /* Indice de l'astuce courante */
+
+    bool uorigin;                           /* Origine de l'affichage      */
+
+};
+
+/* Panneau d'accueil par défaut (classe) */
+struct _GWelcomePanelClass
+{
+    GPanelItemClass parent;                 /* A laisser en premier        */
+
+};
+
+
+/* Colonnes de la liste des messages */
+typedef enum _RecentProjectColumn
+{
+    RPC_VALID,                              /* Validité de l'entrée        */
+    RPC_FULLPATH,                           /* Chemin d'accès à un projet  */
+
+    RPC_COUNT                               /* Nombre de colonnes          */
+
+} RecentProjectColumn;
+
+
+/* Initialise la classe des panneaux d'accueil par défaut. */
+static void g_welcome_panel_class_init(GWelcomePanelClass *);
+
+/* Initialise une instance de panneau d'accueil par défaut. */
+static void g_welcome_panel_init(GWelcomePanel *);
+
+/* Supprime toutes les références externes. */
+static void g_welcome_panel_dispose(GWelcomePanel *);
+
+/* Procède à la libération totale de la mémoire. */
+static void g_welcome_panel_finalize(GWelcomePanel *);
+
+/* Place un panneau dans l'ensemble affiché. */
+static void g_welcome_panel_dock(GWelcomePanel *);
+
+/* Charge l'ensemble des astuces. */
+static void g_welcome_panel_load_tips(GWelcomePanel *);
+
+/* Assure le dessin du fond de la bulle d'astuce. */
+static gboolean on_tip_background_draw(GtkWidget *, cairo_t *, GWelcomePanel *);
+
+/* Réagit à la demande d'étude d'un nouveau binaire. */
+static void on_new_binary_clicked(GtkButton *, GWelcomePanel *);
+
+/* Actualise au besoin la liste des projets récents. */
+static void on_recent_list_changed(GtkRecentManager *, GWelcomePanel *);
+
+/* Recharge une liste à jour des projets récents. */
+static void g_welcome_panel_reload_project_list(GWelcomePanel *, GtkRecentManager *);
+
+/* Réagit à une sélection décidée d'un projet particulier. */
+static void on_row_activated_for_projects(GtkTreeView *, GtkTreePath *, GtkTreeViewColumn *, GWelcomePanel *);
+
+/* Enregistre les conditions d'affichage du panneau d'accueil. */
+static void on_startup_toggled(GtkToggleButton *, GWelcomePanel *);
+
+/* Consulte les versions existantes et affiche une conclusion. */
+static void g_welcome_panel_check_version(GWelcomePanel *);
+
+/* Affiche l'astuce précédente dans la liste globale. */
+static void on_tip_previous_clicked(GtkButton *, GWelcomePanel *);
+
+/* Affiche l'astuce suivante dans la liste globale. */
+static void on_tip_next_clicked(GtkButton *, GWelcomePanel *);
+
+/* Actualise l'affichage des astuces. */
+static void g_welcome_panel_refresh_tip(GWelcomePanel *);
+
+
+/* Indique le type défini pour un panneau d'accueil. */
+G_DEFINE_TYPE(GWelcomePanel, g_welcome_panel, G_TYPE_PANEL_ITEM);
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : klass = classe à initialiser.                                *
+*                                                                             *
+*  Description : Initialise la classe des panneaux d'accueil par défaut.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_class_init(GWelcomePanelClass *klass)
+{
+    GObjectClass *object;                   /* Autre version de la classe  */
+    GPanelItemClass *parent;                /* Version parente de classe   */
+
+    object = G_OBJECT_CLASS(klass);
+
+    object->dispose = (GObjectFinalizeFunc/* ! */)g_welcome_panel_dispose;
+    object->finalize = (GObjectFinalizeFunc)g_welcome_panel_finalize;
+
+    parent = G_PANEL_ITEM_CLASS(klass);
+
+    parent->ack_dock = (ack_undock_process_fc)g_welcome_panel_dock;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance à initialiser.                              *
+*                                                                             *
+*  Description : Initialise une instance de panneau d'accueil par défaut.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_init(GWelcomePanel *panel)
+{
+    GEditorItem *base;                      /* Version basique d'instance  */
+    GPanelItem *pitem;                      /* Version parente du panneau  */
+    GtkTreeView *treeview;                  /* Affichage de la liste       */
+    GtkCellRenderer *renderer;              /* Moteur de rendu de colonne  */
+    GtkTreeViewColumn *column;              /* Colonne de la liste         */
+    GtkToggleButton *button;                /* Bouton à bascule à traiter  */
+    bool state;                             /* Etat de la coche à définir  */
+    gchar *filename;                        /* Chemin d'accès à une image  */
+
+    /* Eléments de base */
+
+    base = G_EDITOR_ITEM(panel);
+
+    base->name = PANEL_WELCOME_ID;
+
+    pitem = G_PANEL_ITEM(panel);
+
+    pitem->personality = PIP_SINGLETON;
+    pitem->lname = _("Welcome");
+    pitem->dock_at_startup = true;
+    pitem->path = strdup("N");
+
+    panel->uorigin = !pitem->dock_at_startup;
+
+    /* Représentation graphique */
+
+    panel->builder = gtk_builder_new_from_resource("/org/chrysalide/gui/panels/welcome.ui");
+
+    base->widget = GTK_WIDGET(gtk_builder_get_object(panel->builder, "box"));
+    g_object_ref(G_OBJECT(base->widget));
+    gtk_widget_unparent(base->widget);
+
+    /* Liste des projets récents */
+
+    treeview = GTK_TREE_VIEW(gtk_builder_get_object(panel->builder, "treeview"));
+
+    column = gtk_tree_view_column_new();
+    gtk_tree_view_append_column(treeview, column);
+    gtk_tree_view_set_expander_column(treeview, column);
+
+    renderer = gtk_cell_renderer_text_new();
+    gtk_tree_view_column_pack_start(column, renderer, TRUE);
+    gtk_tree_view_column_add_attribute(column, renderer, "markup", RPC_FULLPATH);
+
+    /* Affichage au démarrage ? */
+
+    button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(panel->builder, "startup"));
+
+    g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_STARTUP, &state);
+
+    gtk_toggle_button_set_active(button, state);
+
+    /* Chargement de l'image de fond */
+
+    filename = find_pixmap_file("tipoftheday.png");
+
+    panel->background = cairo_image_surface_create_from_png(filename);
+
+    g_free(filename);
+
+    /* Connexion des signaux */
+
+    gtk_builder_add_callback_symbols(panel->builder,
+                                     "on_tip_background_draw", G_CALLBACK(on_tip_background_draw),
+                                     "on_new_binary_clicked", G_CALLBACK(on_new_binary_clicked),
+                                     "on_row_activated_for_projects", G_CALLBACK(on_row_activated_for_projects),
+                                     "on_startup_toggled", G_CALLBACK(on_startup_toggled),
+                                     "on_tip_previous_clicked", G_CALLBACK(on_tip_previous_clicked),
+                                     "on_tip_next_clicked", G_CALLBACK(on_tip_next_clicked),
+                                     NULL);
+
+    gtk_builder_connect_signals(panel->builder, panel);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Supprime toutes les références externes.                     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_dispose(GWelcomePanel *panel)
+{
+    g_object_unref(G_OBJECT(panel->builder));
+
+    free(panel->tips);
+
+    G_OBJECT_CLASS(g_welcome_panel_parent_class)->dispose(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = instance d'objet GLib à traiter.                     *
+*                                                                             *
+*  Description : Procède à la libération totale de la mémoire.                *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_finalize(GWelcomePanel *panel)
+{
+    cairo_surface_destroy(panel->background);
+
+    G_OBJECT_CLASS(g_welcome_panel_parent_class)->finalize(G_OBJECT(panel));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : -                                                            *
+*                                                                             *
+*  Description : Crée un panneau d'accueil par défaut.                        *
+*                                                                             *
+*  Retour      : Adresse de la structure mise en place.                       *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+GPanelItem *g_welcome_panel_new(void)
+{
+    GWelcomePanel *result;                  /* Structure à retourner       */
+    GtkRecentManager *manager;              /* Gestionnaire global         */
+
+    result = g_object_new(G_TYPE_WELCOME_PANEL, NULL);
+
+    manager = get_projects_manager();
+
+    g_signal_connect(manager, "changed", G_CALLBACK(on_recent_list_changed), result);
+
+    g_welcome_panel_reload_project_list(result, manager);
+
+    g_welcome_panel_load_tips(result);
+
+    g_welcome_panel_check_version(result);
+
+    return G_PANEL_ITEM(result);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = composant à présenter à l'affichage.                 *
+*                                                                             *
+*  Description : Place un panneau dans l'ensemble affiché.                    *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_dock(GWelcomePanel *panel)
+{
+    g_welcome_panel_set_user_origin(panel, true);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = panneau d'accueil à mettre à jour.                   *
+*                                                                             *
+*  Description : Charge l'ensemble des astuces.                               *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_load_tips(GWelcomePanel *panel)
+{
+    size_t i;                               /* Boucle de parcours          */
+
+    char *tips[] = {
+
+        _("There is no need to install Chrysalide on your system if you only want to give it a try.\n\n"
+          "Just compile the source code and run the program from there."),
+
+        _("Chrysalide can be used in external Python scripts by setting PYTHONPATH to the directory "
+          "containing the 'pychrysalide.so' file. For instance:\n\n"
+          "  cd plugins/pychrysa/.libs/\n"
+          "  export PYTHONPATH=$PWD\n\n"
+          "Then run the interpreter suitable to your configuration (debug or release):\n\n"
+          "  python3-dbg -c 'import pychrysalide ; print(pychrysalide.mod_version())'"),
+
+        _("All the configuration files for chrysalide are located in $HOME/.config/chrysalide/.")
+
+    };
+
+    panel->count = ARRAY_SIZE(tips);
+
+    panel->tips = (char **)calloc(panel->count, sizeof(char *));
+
+    for (i = 0; i < panel->count; i++)
+        panel->tips[i] = tips[i];
+
+    shuffle(panel->tips, panel->count, sizeof(char *));
+
+    panel->current = 0;
+
+    g_welcome_panel_refresh_tip(panel);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : widget = composant graphique à redessiner.                   *
+*                cr     = contexte graphique à utiliser.                      *
+*                panel  = panneau associé comportant des informations utiles. *
+*                                                                             *
+*  Description : Assure le dessin du fond de la bulle d'astuce.               *
+*                                                                             *
+*  Retour      : FALSE pour poursuivre la propagation de l'événement.         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static gboolean on_tip_background_draw(GtkWidget *widget, cairo_t *cr, GWelcomePanel *panel)
+{
+    int wgt_width;                          /* Largeur disponible totale   */
+    int wgt_height;                         /* Hauteur disponible totale   */
+    int img_width;                          /* Largeur de l'image de fond  */
+    int img_height;                         /* Hauteur de l'image de fond  */
+    double scale;                           /* Echelle à appliquer         */
+
+    if (cairo_surface_status(panel->background) == CAIRO_STATUS_SUCCESS)
+    {
+        wgt_width = gtk_widget_get_allocated_width(widget);
+        wgt_height = gtk_widget_get_allocated_height(widget);
+
+        img_width = cairo_image_surface_get_width(panel->background);
+        img_height = cairo_image_surface_get_height(panel->background);
+
+        scale = wgt_height / (2.0 * img_height);
+
+        cairo_scale(cr, scale, scale);
+
+        cairo_set_source_surface(cr, panel->background,
+                                 (wgt_width / scale) - img_width,
+                                 ((wgt_height / scale) - img_height) / 2);
+
+        cairo_paint(cr);
+
+    }
+
+    return FALSE;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton impliqué dans la procédure.                  *
+*                panel  = panneau associé comportant des informations utiles. *
+*                                                                             *
+*  Description : Réagit à la demande d'étude d'un nouveau binaire.            *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_new_binary_clicked(GtkButton *button, GWelcomePanel *panel)
+{
+    GObject *ref;                           /* Espace de référencements    */
+    GtkMenuItem *item;                      /* Elément de menu simulé      */
+
+    ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(panel));
+
+    item = GTK_MENU_ITEM(g_object_get_data(ref, "mnu_project_add_binary"));
+
+    gtk_menu_item_activate(item);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : manager = gestion de fichiers récemment utilisés.            *
+*                panel   = panneau associé comportant des informations utiles.*
+*                                                                             *
+*  Description : Actualise au besoin la liste des projets récents.            *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_recent_list_changed(GtkRecentManager *manager, GWelcomePanel *panel)
+{
+    g_welcome_panel_reload_project_list(panel, manager);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel   = panneau comportant des informations utiles.        *
+*                manager = gestion de fichiers récemment utilisés.            *
+*                                                                             *
+*  Description : Recharge une liste à jour des projets récents.               *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_reload_project_list(GWelcomePanel *panel, GtkRecentManager *manager)
+{
+    GtkListStore *store;                    /* Modèle de gestion           */
+    bool empty;                             /* Liste vide ?                */
+    GList *recents;                         /* Liste des fichiers récents  */
+    GList *recent;                          /* Elément à traiter           */
+    GtkRecentInfo *info;                    /* Informations sur l'élément  */
+    GtkTreeIter iter;                       /* Point d'insertion           */
+
+    /* Réinitialisation */
+
+    store = GTK_LIST_STORE(gtk_builder_get_object(panel->builder, "store"));
+
+    gtk_list_store_clear(store);
+
+    empty = true;
+
+    /* Chargement */
+
+    recents = gtk_recent_manager_get_items(manager);
+
+    if (recents != NULL)
+    {
+        for (recent = g_list_first(recents); recent != NULL; recent = g_list_next(recent))
+        {
+            info = recent->data;
+
+            if (strcmp(gtk_recent_info_get_mime_type(info), "application/chrysalide.project") == 0)
+            {
+                gtk_list_store_append(store, &iter);
+
+                gtk_list_store_set(store, &iter,
+                                   RPC_VALID, true,
+                                   RPC_FULLPATH, gtk_recent_info_get_uri_display(info),
+                                   -1);
+
+                empty = false;
+
+            }
+
+            gtk_recent_info_unref(info);
+
+        }
+
+        g_list_free(recents);
+
+    }
+
+    /* Indication par défaut */
+    if (empty)
+    {
+        gtk_list_store_append(store, &iter);
+
+        gtk_list_store_set(store, &iter,
+                           RPC_VALID, false,
+                           RPC_FULLPATH, _("<i>(No recent project)</i>"),
+                           -1);
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : treeview = liste graphique concernée par la procédure.       *
+*                path     = chemin d'accès à la ligne sélectionnée.           *
+*                column   = colonne concernée par la sélection.               *
+*                panel    = panneau associé avec des informations utiles.     *
+*                                                                             *
+*  Description : Réagit à une sélection décidée d'un projet particulier.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_row_activated_for_projects(GtkTreeView *treeview, GtkTreePath *path, GtkTreeViewColumn *column, GWelcomePanel *panel)
+{
+    GtkTreeModel *model;                    /* Modèle de gestion           */
+    GtkTreeIter iter;                       /* Point de la consultation    */
+    gboolean valid;                         /* Validité de l'entrée        */
+    gchar *filename;                        /* Chemin d'accès au projet    */
+    GObject *ref;                           /* Espace de référencements    */
+    GStudyProject *project;                 /* Nouveau projet à ouvrir     */
+
+    model = gtk_tree_view_get_model(treeview);
+
+    if (gtk_tree_model_get_iter(model, &iter, path))
+    {
+        gtk_tree_model_get(model, &iter, RPC_VALID, &valid, RPC_FULLPATH, &filename, -1);
+
+        if (valid)
+        {
+            ref = g_editor_item_get_global_ref(G_EDITOR_ITEM(panel));
+
+            project = g_study_project_open(ref, filename);
+
+            if (project != NULL)
+            {
+                set_current_project(project);
+
+                push_project_into_recent_list(project);
+
+            }
+
+            g_free(filename);
+
+        }
+
+    }
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton de défilement des astuces activé;            *
+*                panel  = panneau associé comportant des informations utiles. *
+*                                                                             *
+*  Description : Enregistre les conditions d'affichage du panneau d'accueil.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_startup_toggled(GtkToggleButton *button, GWelcomePanel *panel)
+{
+    g_generic_config_set_value(get_main_configuration(),
+                               MPK_WELCOME_STARTUP, gtk_toggle_button_get_active(button));
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = panneau d'accueil à mettre à jour.                   *
+*                                                                             *
+*  Description : Consulte les versions existantes et affiche une conclusion.  *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_check_version(GWelcomePanel *panel)
+{
+    bool skip;                              /* Saut de la vérification     */
+    bool unknown;                           /* Impossibilité de comparaison*/
+    int current;                            /* Version courante            */
+    int sock;                               /* Canal de communication      */
+    bool status;                            /* Bilan d'une communication   */
+    char buffer[1024];                      /* Tampon de réception         */
+    size_t got;                             /* Quantité de données reçues  */
+    char *version;                          /* Version récupérée           */
+    int available;                          /* Version disponible          */
+    GtkLabel *label;                        /* Etiquette à éditer          */
+    char *msg;                              /* Message à faire paraître    */
+
+    g_generic_config_get_value(get_main_configuration(), MPK_WELCOME_CHECK, &skip);
+    skip = !skip;
+
+    unknown = true;
+
+    current = atoi(VERSION);
+
+    if (skip) goto check_process;
+
+    /* Recherche en ligne */
+
+    sock = connect_via_tcp("www.chrysalide.re", "80", NULL);
+    if (sock == -1) goto check_process;
+
+#define REQUEST "GET /version.last HTTP/1.1\r\nHost: www.chrysalide.re\r\n\r\n"
+
+    status = safe_send(sock, REQUEST, strlen(REQUEST), 0);
+    if (!status) goto check_done;
+
+    status = recv_all(sock, buffer, sizeof(buffer), &got);
+    if (!status) goto check_done;
+
+    version = strstr(buffer, "\r\n\r\n");
+
+    if (version != NULL)
+    {
+        available = atoi(version + 4);
+
+        unknown = false;
+
+    }
+
+ check_done:
+
+    close(sock);
+
+ check_process:
+
+    /* Affichage */
+
+    label = GTK_LABEL(gtk_builder_get_object(panel->builder, "version"));
+
+    if (skip)
+        asprintf(&msg,
+                 "Your version is: <b>%d</b>\n\n"               \
+                 "Automatic version check is disabled.",
+                 current);
+
+    else
+    {
+        if (unknown)
+            asprintf(&msg,
+                     "Your version is: <b>%d</b>\n\n"           \
+                     "Lastest available version is unknown.",
+                     current);
+
+        else
+        {
+            if (current >= available)
+                asprintf(&msg,
+                         "Your version is: <b>%d</b>\n\n"       \
+                         "Lastest version is: <b>%d</b>\n\n"    \
+                         "Your software is <span color='green'><b>up-to-date</b></span>.",
+                         current, available);
+
+            else
+                asprintf(&msg,
+                         "Your version is: <b>%d</b>\n\n"       \
+                         "Lastest version is: <b>%d</b>\n\n"    \
+                         "Your software is <span color='red'><b>outdated</b></span>.",
+                         current, available);
+
+        }
+
+    }
+
+    gtk_label_set_markup(label, msg);
+
+    free(msg);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton de défilement des astuces activé;            *
+*                panel  = panneau associé comportant des informations utiles. *
+*                                                                             *
+*  Description : Affiche l'astuce précédente dans la liste globale.           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_tip_previous_clicked(GtkButton *button, GWelcomePanel *panel)
+{
+    if (panel->current > 0)
+        panel->current--;
+    else
+        panel->current = panel->count - 1;
+
+    g_welcome_panel_refresh_tip(panel);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : button = bouton de défilement des astuces activé;            *
+*                panel  = panneau associé comportant des informations utiles. *
+*                                                                             *
+*  Description : Affiche l'astuce suivante dans la liste globale.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void on_tip_next_clicked(GtkButton *button, GWelcomePanel *panel)
+{
+    if ((panel->current + 1) < panel->count)
+        panel->current++;
+    else
+        panel->current = 0;
+
+    g_welcome_panel_refresh_tip(panel);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = panneau associé comportant des informations utiles.  *
+*                                                                             *
+*  Description : Actualise l'affichage des astuces.                           *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void g_welcome_panel_refresh_tip(GWelcomePanel *panel)
+{
+    GtkLabel *label;                        /* Etiquette de présentation   */
+
+    assert(panel->current < panel->count);
+
+    label = GTK_LABEL(gtk_builder_get_object(panel->builder, "tip"));
+
+    gtk_label_set_markup(label, panel->tips[panel->current]);
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel = panneau associé comportant des informations utiles.  *
+*                                                                             *
+*  Description : Indique l'origine de l'affichage du panneau d'accueil.       *
+*                                                                             *
+*  Retour      : true si l'affichage est le fait de l'utilisateur.            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool g_welcome_panel_get_user_origin(const GWelcomePanel *panel)
+{
+    return panel->uorigin;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : panel   = panneau associé comportant des informations utiles.*
+*                uorigin = true si l'affichage est le fait de l'utilisateur.  *
+*                                                                             *
+*  Description : Détermine l'origine de l'affichage du panneau d'accueil.     *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+void g_welcome_panel_set_user_origin(GWelcomePanel *panel, bool uorigin)
+{
+    panel->uorigin = uorigin;
+
+}
diff --git a/src/gui/panels/welcome.h b/src/gui/panels/welcome.h
new file mode 100644
index 0000000..c75d6f5
--- /dev/null
+++ b/src/gui/panels/welcome.h
@@ -0,0 +1,68 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * welcome.h - prototypes pour le panneau d'accueil par défaut
+ *
+ * Copyright (C) 2016 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  OpenIDA is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  OpenIDA is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _GUI_PANELS_WELCOME_H
+#define _GUI_PANELS_WELCOME_H
+
+
+#include <i18n.h>
+
+
+#include "panel.h"
+
+
+
+#define PANEL_WELCOME_ID _("Welcome")
+
+
+#define G_TYPE_WELCOME_PANEL               g_welcome_panel_get_type()
+#define G_WELCOME_PANEL(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj), g_welcome_panel_get_type(), GWelcomePanel))
+#define G_IS_WELCOME_PANEL(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj), g_welcome_panel_get_type()))
+#define G_WELCOME_PANEL_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass), G_TYPE_WELCOME_PANEL, GWelcomePanelClass))
+#define G_IS_WELCOME_PANEL_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass), G_TYPE_WELCOME_PANEL))
+#define G_WELCOME_PANEL_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS((obj), G_TYPE_WELCOME_PANEL, GWelcomePanelClass))
+
+
+/* Panneau d'accueil par défaut (instance) */
+typedef struct _GWelcomePanel GWelcomePanel;
+
+/* Panneau d'accueil par défaut (classe) */
+typedef struct _GWelcomePanelClass GWelcomePanelClass;
+
+
+/* Indique le type défini pour un panneau d'accueil. */
+GType g_welcome_panel_get_type(void);
+
+/* Crée un panneau d'accueil par défaut. */
+GPanelItem *g_welcome_panel_new(void);
+
+/* Indique l'origine de l'affichage du panneau d'accueil. */
+bool g_welcome_panel_get_user_origin(const GWelcomePanel *);
+
+/* Détermine l'origine de l'affichage du panneau d'accueil. */
+void g_welcome_panel_set_user_origin(GWelcomePanel *, bool);
+
+
+
+#endif  /* _GUI_PANELS_WELCOME_H */
diff --git a/src/gui/panels/welcome.ui b/src/gui/panels/welcome.ui
new file mode 100644
index 0000000..d016907
--- /dev/null
+++ b/src/gui/panels/welcome.ui
@@ -0,0 +1,245 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.18.3 -->
+<interface>
+  <requires lib="gtk+" version="3.12"/>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="can_focus">False</property>
+    <property name="stock">gtk-new</property>
+    <property name="icon_size">6</property>
+  </object>
+  <object class="GtkListStore" id="store">
+    <columns>
+      <!-- column-name valid -->
+      <column type="gboolean"/>
+      <!-- column-name fullpath -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkOffscreenWindow" id="offscreenwindow1">
+    <property name="can_focus">False</property>
+    <child>
+      <object class="GtkBox" id="box">
+        <property name="visible">True</property>
+        <property name="can_focus">False</property>
+        <property name="homogeneous">True</property>
+        <child>
+          <object class="GtkBox" id="box2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">8</property>
+            <property name="margin_right">8</property>
+            <property name="margin_top">8</property>
+            <property name="margin_bottom">8</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">8</property>
+            <child>
+              <object class="GtkAlignment" id="alignment1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="top_padding">180</property>
+                <property name="bottom_padding">30</property>
+                <child>
+                  <object class="GtkButton" id="button1">
+                    <property name="label" translatable="yes">New binary...</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <property name="image">image1</property>
+                    <property name="image_position">top</property>
+                    <property name="always_show_image">True</property>
+                    <signal name="clicked" handler="on_new_binary_clicked" swapped="no"/>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes">Last projects:</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkTreeView" id="treeview">
+                <property name="height_request">250</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="margin_left">8</property>
+                <property name="hexpand">True</property>
+                <property name="model">store</property>
+                <property name="headers_visible">False</property>
+                <property name="rules_hint">True</property>
+                <signal name="row-activated" handler="on_row_activated_for_projects" swapped="no"/>
+                <child internal-child="selection">
+                  <object class="GtkTreeSelection" id="treeview-selection1"/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkCheckButton" id="startup">
+                <property name="label" translatable="yes">Show this panel at startup.</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">False</property>
+                <property name="halign">start</property>
+                <property name="valign">end</property>
+                <property name="vexpand">True</property>
+                <property name="xalign">0</property>
+                <property name="draw_indicator">True</property>
+                <signal name="toggled" handler="on_startup_toggled" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">3</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox" id="box3">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="margin_left">8</property>
+            <property name="margin_right">8</property>
+            <property name="margin_top">8</property>
+            <property name="margin_bottom">8</property>
+            <property name="orientation">vertical</property>
+            <property name="spacing">8</property>
+            <child>
+              <object class="GtkBox" id="box4">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="homogeneous">True</property>
+                <child>
+                  <object class="GtkLabel" id="version">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="opacity">0.81999999977648264</property>
+                    <property name="use_markup">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkLabel" id="label3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="opacity">0.81999999999999995</property>
+                    <property name="label" translatable="yes">Get access to the online documentation and stay tuned by visiting the official website : &lt;a href="http://chrysalide.re"&gt;chrysalide.re&lt;/a&gt;
+
+You can also follow Chrysalide on Twitter : &lt;a href="http://twitter.com/chrysalide_ref"&gt;@chrysalide_ref&lt;/a&gt;</property>
+                    <property name="use_markup">True</property>
+                    <property name="wrap">True</property>
+                    <property name="track_visited_links">False</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="tip">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="xalign">0</property>
+                <property name="yalign">1</property>
+                <property name="xpad">8</property>
+                <property name="ypad">8</property>
+                <property name="label" translatable="yes">label</property>
+                <property name="wrap">True</property>
+                <property name="selectable">True</property>
+                <signal name="draw" handler="on_tip_background_draw" swapped="no"/>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButtonBox" id="buttonbox1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="spacing">8</property>
+                <property name="layout_style">end</property>
+                <child>
+                  <object class="GtkButton" id="button3">
+                    <property name="label" translatable="yes">Previous</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <signal name="clicked" handler="on_tip_previous_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkButton" id="button2">
+                    <property name="label" translatable="yes">Next</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">True</property>
+                    <signal name="clicked" handler="on_tip_next_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">True</property>
+                    <property name="fill">True</property>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
diff --git a/src/main.c b/src/main.c
index 821773d..a4dfdde 100644
--- a/src/main.c
+++ b/src/main.c
@@ -103,8 +103,7 @@ int main(int argc, char **argv)
     char *author;                           /* Identification à diffuser   */
     char *pub;                              /* Chemin de la clef publique  */
 
-    const char *filename;                   /* Chemin du dernier projet    */
-    GStudyProject *project;                 /* Nouveau projet courant      */
+    bool welcome;                           /* Affichage de la bienvenue ? */
 
     result = EXIT_FAILURE;
 
@@ -197,10 +196,12 @@ int main(int argc, char **argv)
     server = g_db_server_new_internal(author, pub);
     g_db_server_start(server);
 
-    /* Charge le dernier projet */
+    /* Charge le dernier projet ? */
 
     gboolean load_last_project(GGenConfig *cfg)
     {
+        const char *filename;               /* Chemin du dernier projet    */
+        GStudyProject *project;             /* Nouveau projet courant      */
 
         if (!g_generic_config_get_value(cfg, MPK_LAST_PROJECT, &filename))
             filename = NULL;
@@ -214,7 +215,13 @@ int main(int argc, char **argv)
 
     }
 
-    g_idle_add((GSourceFunc)load_last_project, config);
+    g_generic_config_get_value(config, MPK_WELCOME_STARTUP, &welcome);
+
+    if (!welcome)
+        g_idle_add((GSourceFunc)load_last_project, config);
+
+    else
+        set_current_project(g_study_project_new(G_OBJECT(editor)));
 
     /* Exécution du programme */
 
-- 
cgit v0.11.2-87-g4458