diff options
32 files changed, 1571 insertions, 670 deletions
@@ -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 Binary files differindex 12f017a..12f017a 100644 --- a/plugins/python/welcome/tipoftheday.png +++ b/pixmaps/tipoftheday.png diff --git a/plugins/python/welcome/tipoftheday.xcf b/pixmaps/tipoftheday.xcf Binary files differindex dc9cf06..dc9cf06 100644 --- a/plugins/python/welcome/tipoftheday.xcf +++ b/pixmaps/tipoftheday.xcf 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 : <a href="http://chrysalide.re">chrysalide.re</a> + +You can also follow Chrysalide on Twitter : <a href="http://twitter.com/chrysalide_ref">@chrysalide_ref</a></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> @@ -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 */ |