summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2016-09-21 19:29:39 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2016-09-21 19:29:39 (GMT)
commitb6afbe8c699ae76443628badae33beee9934c6bc (patch)
tree69878624d3ae529314a9824f0c0ceca8bca617b7
parent291968f4f4a5e85f6963813a43f2176320fb8d49 (diff)
Provided a real welcome panel at startup, with tricks and actions.
-rw-r--r--ChangeLog68
-rw-r--r--configure.ac1
-rw-r--r--pixmaps/tipoftheday.png (renamed from plugins/python/welcome/tipoftheday.png)bin10206 -> 10206 bytes
-rw-r--r--pixmaps/tipoftheday.xcf (renamed from plugins/python/welcome/tipoftheday.xcf)bin17942 -> 17942 bytes
-rw-r--r--plugins/python/Makefile.am2
-rw-r--r--plugins/python/welcome/Makefile.am14
-rw-r--r--plugins/python/welcome/__init__.py4
-rw-r--r--plugins/python/welcome/binary.py40
-rw-r--r--plugins/python/welcome/board.py102
-rw-r--r--plugins/python/welcome/panel.py148
-rw-r--r--plugins/python/welcome/plugin.py36
-rw-r--r--plugins/python/welcome/tip.py146
-rw-r--r--plugins/python/welcome/version.py116
-rw-r--r--plugins/python/welcome/website.py30
-rwxr-xr-xsrc/common/Makefile.am1
-rw-r--r--src/common/net.c57
-rw-r--r--src/common/net.h4
-rw-r--r--src/common/shuffle.c78
-rw-r--r--src/common/shuffle.h37
-rw-r--r--src/core/params.c6
-rw-r--r--src/core/params.h2
-rw-r--r--src/gui/core/panels.c4
-rw-r--r--src/gui/editor.c109
-rw-r--r--src/gui/panels/Makefile.am19
-rw-r--r--src/gui/panels/gresource.xml6
-rw-r--r--src/gui/panels/panel-int.h10
-rw-r--r--src/gui/panels/panel.c6
-rw-r--r--src/gui/panels/regedit.c2
-rw-r--r--src/gui/panels/welcome.c865
-rw-r--r--src/gui/panels/welcome.h68
-rw-r--r--src/gui/panels/welcome.ui245
-rw-r--r--src/main.c15
32 files changed, 1571 insertions, 670 deletions
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/plugins/python/welcome/tipoftheday.png b/pixmaps/tipoftheday.png
index 12f017a..12f017a 100644
--- a/plugins/python/welcome/tipoftheday.png
+++ b/pixmaps/tipoftheday.png
Binary files differ
diff --git a/plugins/python/welcome/tipoftheday.xcf b/pixmaps/tipoftheday.xcf
index dc9cf06..dc9cf06 100644
--- a/plugins/python/welcome/tipoftheday.xcf
+++ b/pixmaps/tipoftheday.xcf
Binary files 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/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 */