wxDev.fr, le portail francophone consacré à wxWidgets ! ( The french portal for wxWidgets )  
Esp. membre
Recheche rapide



Recherche avancée
Statistiques
Membres inscrits :2359

Membres en ligne : 0
Invités en ligne : 2

Valid XHTML 1.0 Transitional

Valid CSS2

Menu forum (navigation):
Pages: 1  
 
Accueil » Accueil forums » Utilisation générale wxWidgets
» AuiToolbarItem liés à des entrées de menu
Conversation (Résolue) : AuiToolbarItem liés à des entrées de menu
25-01-2012 12:13:11  AuiToolbarItem liés à des entrées de menu #1
freem (Membre)
Inscrit le : 09-04-2010
Messages: 11
Snippets: 0
Tutoriels: 0
Hors ligne
Je voulais savoir si il existe une classe, ou un système, pour lier (je devrai plutôt dire regrouper dans un même objet) une entrée de menu et un bouton de toolbar (auiToolbar dans le cas qui m'intéresse le plus).

Pour prendre un exemple simple:
Dans word (ancien style, sans le bandeau) il y avais un menu "Fichier" contenant l'entrée "Sauvegarder" ainsi qu'un bouton de raccourcis dans l'application qui faisait la même chose (sisi celui avec la disquette... bon je blague je sais que j'ai pas besoin de préciser :P ).

La seule solution que je connais actuellement avec wxWidgets est de définir séparément chacune de ces deux entités, et pour chacune d'elle lier leur évènement d'activation à la fonction de sauvegarde.

Pour une applis ou ce type de doublon est occasionnel, pourquoi pas... Ca fait du code lourd à maintenir, imposant et peu puissant, mais pourquoi pas...
Le problème, c'est que je veux (et j'ai déjà commencé d'ailleurs) refaire AutoRealm from scratch, en C++ (histoire de le rendre utilisable nativement sous Linux, d'une part, et de corriger certains problèmes de performance et de rendu, d'autre part.) et ce logiciel possède de très nombreux doublons de ce genre.

Un système qui me permettrait de n'avoir qu'un seul objet qui gère ces deux entrées me permettrait plusieurs choses bien sympathiques:
_ créer un système de personnalisation des boîtes à outils qui gère les boutons un par un (je crois que word avait ça... mais je ne l'ai que peu utilisé donc j'ai un doute)
_ optimiser certaines données (chaînes de caractère - bulles d'aide par exemple - non dupliquées, ID pour une fonctionnalité et pas pour une entrée) et faciliter la traduction
_ augmenter la maintenabilité du code
_ créer dynamiquement au bon vouloir de l'utilisateur les barres d'outils et y mettre les raccourcis voulus sans distinction de groupe

Et peut-être d'autres que je ne perçois pas encore.

J'ai bien pensé à créer une classe qui hérite de wxMenuItem et qui contient les données nécessaires à wxAuiToolbar->AddItem, mais je me dis que ça existe peut-être déjà... sans compter que wxAuiToolbar ne semble pas posséder de méthode AddItem prenant en paramètre un objet, je devrai donc filer l'adresse de la wxAuiToolbar en question à mon objet perso pour lui permettre de s'enregistrer dedans. Du coup pour l'en retirer, il faut que cet objet conserve une liste de pointeur sur les wxAuiToolbar qui le "contiennent" afin de pouvoir s'en enlever... (bon, vous me direz, un std::list<wxAuiToolbar*> c'est pas compliqué à gérer, mais quand même)

Voila, donc si quelqu'un à une idée, je suis preneur.

(Je ne met pas le lien vers mon projet parce qu'il n'est pas assez avancé pour être utilisable (pour le moment je soigne plus la conception que la fonctionnalité déjà implémentée, histoire de pouvoir réutiliser au max le code)... Sauf si quelqu'un tiens à l'avoir)

[edit]
Je me suis planté de section je pense...Et pas moyen de déplacer moi-même :/ donc mea culpa si mauvais endroit

Dernière modification par freem (25-01-2012 12:21:07)

25-01-2012 14:54:10  Re: AuiToolbarItem liés à des entrées de menu #2
Xaviou (Administrateur)
Lieu: Annecy (74)
Inscrit le : 27-08-2007
Messages: 1387
Snippets: 25
Tutoriels: 6
Site web
Hors ligne
Salut.

C'est vrai que le fait d'avoir à répéter la majorité des chaînes de caractères n'est "pas top".

En ce qui concerne le fait d'utiliser un ID pour une fonctionnalité et non pour une entrée, c'est faisable sans rien toucher au code : une entrée de menu et un bouton de barre d'outils peuvent très bien avoir le même identifiant.
Je ne sais pas ce qu'il en est avec une wxAuiToolbar, mais avec une wxToolBar classique, il n'est même pas nécessaire d'avoir les deux entrées dans la table d'événements (ou d'appeler deux fois la méthode "Connect").

Ensuite, pour ce qui est le la table des événements, rien ne t'empêche de placer toutes les entrées (correspondant à tous les identifiants dont tu te sers) même si certains ne sont pas utilisés dans la barre d'outils (ça évite d'avoir à Connecter / Déconnecter certains événements lors de la personnalisation).

En ce qui concerne les chaînes de caractères, tu peux utiliser une (ou plusieurs) wxHashMap avec en index, l'identifiant (ce qui implique d'utiliser des valeurs constantes prédéfinies) et en valeur la chaîne concernée.

Tu pourrais par exemple avoir une map pour les "titres" des menus et qui pourraient éventuellement être affichés avec la barre d'outils, une autre pour la description de chaque fonctionnalité (affichée dans la barre d'état lorsque la souris survole une entrée de menu et affichée sous forme de tooltip lorsque la souris survole un bouton de la barre d'outils).

Enfin, pour "enregistrer" la personnalisation (toujours sur le principe d'une table prédéfinie d'identifiants), il te suffirait de stocker les identifiants correspondants aux éléments à afficher (avec éventuellement une liste prédéfinie d'éléments à afficher par défaut si aucune personnalisation n'est faite par l'utilisateur).

Enfin, c'est juste une solution qui m'est venue à l'esprit en lisant ton post.

@+
Xav'

Le nouveau portail wxWidgets francophone : www.wxdev.fr
Ben en fait, vous y êtes déjà... et effectivement, depuis le temps, ce n'est plus tellement nouveau....
28-01-2012 23:12:20  Re: AuiToolbarItem liés à des entrées de menu #3
freem (Membre)
Inscrit le : 09-04-2010
Messages: 11
Snippets: 0
Tutoriels: 0
Hors ligne
Désolé pour le délai de réponse...

Pour le coup, je pense que je vais dériver une classe de wxMenuEntry qui possède les informations (redondantes pour certaines) nécessaires à créer le toolbarItem lié.

J'ai commencé à réfléchir à ce point, mais je dois reconnaître que je vais devoir jouer la carte de l'expérimentation plus que de la réflexion, je ne parviens pas trop a visualiser le fonctionnement des toolbar... (aui ou pas)
Peut-être que je vais utiliser un outil pour générer le diagramme des classes du code source...

Enfin, je pense que je re posterai ici quand j'aurai un truc qui semble correct.

[edit]
En fait, je n'arrive pas à trouver de doc tout court au sujet de wxAuiToolbarItem.
Je dois regarder dans les source pour essayer d'en comprendre les possibilités, ou il existe une doc sur cette classe quelque part?
Il semble qu'elle hérite de wxControl, qui hérite de wxControlBase, qui elle-même hérite de wxWindow...
Pas même de référence à une classe wxAuiXXX dans la chaîne d'héritage, je ne comprend pas trop le modèle qui l'inclut pour le coup...
Je vais tenter de trouver de la doc sur ces mécanismes Aui, qui sont quand même très intéressants par rapport à la souplesse qu'ils trouvent, sinon je me laisse une semaine pour trouver le moyen de faire une classe qui fasse ce dont j'ai besoin, avant d'utiliser une méthode plus barbare, parce que ce n'est pas en bloquant sur un détail aussi '''insignifiant''' que l'UI qu'autorealm pourra renaître :/ (le pire c'est que pour ce qui est lié au graphisme, j'ai une modélisation qui tiens super bien la route... jamais aimé les UI... toujours compliqué pour pas grand chose, mais un outil de dessin en ligne de commande serait... ahem...bref)
----------
Pour résoudre mon problème de code dupliqué & co plus tard (je pense ne pas tout avoir saisi au sujet de ta solution, d'où mon manque d'entrain à l'appliquer...) je vais créer mon architecture logicielle pour pouvoir supporter des plugin.
Du coup, le problème est déplacé en attendant plus propre, vu que mes codes dupliqués ne seront plus situés dans le projet lui-même, mais dans des plug-in. Au moins le coeur sera propre, et quand ça commencera a tourner et a avoir quelques outils, je pense que j'aurai une idée pour résoudre définitivement le problème, au prix potentiel de la réécriture de plusieurs de ces entrées.

Je ne sais pas trop si je dois mettre résolu ou pas pour le coup, mais je tenait a préciser que je ne considère plus ce problème comme bloquant. (Mon objectif n°1 étant d'avoir un coeur propre et stable, décaler le problème me conviens pour le moment.)
----------
Encore désolé pour le délai... Mais je viens tout juste (enfin, il y a 2 jours) de régler une partie du problème.

J'ai créé un code qui permet en gros de créer une instance de classe "Item", qui récupère (dans une classe fille) les données concernant l'entrée (donc, du wxAuiToolbarItem et du wxMenuItem) et celle relative au "chemin" ou cet Item s'enregistrera (par exemple, pour un menu: Tool / Drawing / Shape / Line / [PolyLineTool] ou [PolyLineTool]  est le nom de l'item).
Dans le cas du wxMenuItem, le chemin reproduit avec un menu par entrée. Dans le cas du wxAuiToolbarItem, le nom de la wxAuiToolbar sera celui du dernier membre du chemin (Line dans l'exemple).
Les entrées sont créées si elles n'existent pas.

Basiquement, l'intérêt est qu'il n'y a plus cette satanée répétition de chaînes de caractères dans le code, et en plus, ça permet de créer des plug-in qui n'ont chacun qu'une seule fonctionnalité, une seule entrée.
(dans le genre faciliter mon boulot sur autorealm, je pense que ça va être plutôt sympa)
Je pense même que ça va, à terme, et avec un peu d'évolution, me permettre de gérer la configuration de certains plug-in de façon automatiquement centralisée (pour ceux qui connaissent autoRealm - pour les autres, c'est un logiciel de dessin vectoriel open-source, écrit en delphi, dont le but original est de dessiner des cartes de jeu de rôle. Essayez-le donc. - , vous aurez remarqué que certains boutons sont en fait des contrôles. Ce système permettra de les isoler et même de centraliser les options dans un dialogue, structuré de la même façon que la position du bouton dans le menu, le tout dynamiquement. Ca inclut le chargement/déchargement de plug-in - mais c'est pour le futur ;) )

Actuellement, mon code a juste un "petit" problème: la gestion des évènements (avec Bind) ne fonctionne pas, pour une raison qui m'est inconnue (j'ai demandé de l'aide sur le forum anglais).

Si jamais quelqu'un ici à une idée... voici le code:

Code:

 
class Container : public std::pair<wxAuiToolBar* ,wxAuiPaneInfo>
{
};
 
struct MenuData
{
    MenuData(std::string const &nAME, std::string const& hELP, wxItemKind const kIND)
    :kind(kIND),help(hELP),name(nAME){}
 
    MenuData(void)
    :kind(),help(),name(){}
 
    wxItemKind kind;
    std::string help;
    std::string name;
};
 
class Item: public wxEvtHandler
{
public:
    Item(void);
    void registerIn(wxFrame *parent,std::map<std::string,Container>&,AppConfig const& appConfig);
 
    void enable(void);
    virtual void readConfig(std::string const &graphicalResources)=0;
    void createMenu(void);
    void createToolbarItem(std::map<std::string,Container>&containers);
 
public:
    wxFrame * m_parent;
///common parameters
    MenuData m_entry;
    long m_id;
 
///menu parameters
    std::vector<MenuData> m_path;
 
///toolbar parameters
    std::string m_longDoc;
    wxObject *m_unused;
    void (Item::*m_callback)(wxCommandEvent&);
    wxBitmap m_disabled,m_enabled;
};
 
Item::Item(void)
:m_id(wxNewId())
{
}
 
void Item::registerIn(wxFrame *parent,std::map<std::string,Container>&containers,AppConfig const& appConfig)
{
    m_parent=parent;
    readConfig(appConfig.m_graphicalResources);
 
    createMenu();
    createToolbarItem(containers);
    enable();
}
 
void Item::createMenu(void)
{
    wxMenu *target=NULL;
    wxMenuBar *menubar=m_parent->GetMenuBar();
    int targetIndex;
 
//ensure the existency of the menu path
    std::vector<MenuData>::iterator it=m_path.begin();
    if(wxNOT_FOUND==(targetIndex=menubar->FindMenu(it->name)))
    {
        menubar->Append(new wxMenu(),it->name);
        targetIndex=menubar->FindMenu(it->name);
        if(wxNOT_FOUND==targetIndex)
            throw std::runtime_error(std::string("unable to create menu: ")+it->name);
    }
    target=menubar->GetMenu(targetIndex);
    ++it;
 
    for(;it!=m_path.end();++it)
    {
        if(wxNOT_FOUND==(targetIndex=target->FindItem(it->name)))
        {
            // create all submenus
            for(;it!=m_path.end();++it)
            {
                target->Append(
                        new wxMenuItem(
                                target,
                                wxNewId(),
                                it->name,
                                it->help,
                                it->kind,
                                target
                                )
                        );
                targetIndex=target->FindItem(it->name);
                if(wxNOT_FOUND==targetIndex)
                    throw std::runtime_error(std::string("unable to create menu: ")+it->name);
                target=menubar->GetMenu(targetIndex);
            }
        }
        target=menubar->GetMenu(targetIndex);
    }
    //create menu item
    wxMenuItem *tmp=new wxMenuItem(target,0,m_entry.name,m_entry.help,m_entry.kind,0);
    target->Append(tmp);
}
 
void Item::createToolbarItem(std::map<std::string,Container>&containers)
{
    std::map<std::string,Container>::iterator it=containers.find(m_entry.name);
    if(it==containers.end())
    {
        //create & register container
        Container c;
        c.first=new wxAuiToolBar(m_parent);
        c.second=wxAuiPaneInfo().Name(m_path.rbegin()->name).ToolbarPane().Caption(m_path.rbegin()->name).Layer(10).Top().Gripper();
        containers[m_entry.name]=c;
    }
    //create the item
    containers[m_entry.name].first->AddTool(m_id, m_entry.name, m_enabled, m_disabled, m_entry.kind, m_entry.name, m_longDoc, m_unused);
 
    //insert the item inside the container
    wxAuiManager::GetManager(m_parent)->AddPane(containers[m_entry.name].first,containers[m_entry.name].second);
}
 
void Item::enable(void)
{
    Bind(wxEVT_COMMAND_MENU_SELECTED, m_callback, this, m_id);
}
 

Note: AppConfig est actuellement une structure ne contenant que le chemin d'accès aux ressources graphiques (std::string).

Le code pour l'application hôte:

Code:

 
    std::vector<ItemProvider*>::iterator ita;
    for(ita=m_actionProviders.begin();ita!=m_actionProviders.end();++ita)
    {
        m_items.push_back((*ita)->create());
        (*m_items.rbegin())->registerIn(this,m_containers,m_appConfig);
    }
    for(std::map<std::string,Container>::iterator it=m_containers.begin();it!=m_containers.end();++it)
        it->second.first->Realize();
    m_auiManager.Update();
 

Note: m_items est déclaré en attribut de classe: "std::vector<Item* > m_items;"

Le code pour le plug-in (qui hérite de Item) :

Code:

 
void PolyLineTool::readConfig(std::string const &graphicalResources)
{
    m_entry.help="help about polylinetool";
    m_entry.kind=wxITEM_NORMAL;
    m_entry.name="polylinetool";
    m_id=wxNewId();
 
    m_path.push_back(MenuData("Tool","tool menu",wxITEM_NORMAL));
 
    m_longDoc="long doc about polylinetool";
    m_disabled=wxNullBitmap;
    m_enabled=wxImage(graphicalResources+"png_files/toolbars/shape/tool-polycurve.png");
 
    m_callback=static_cast<void(Item::*)(wxCommandEvent&)>(&PolyLineTool::onClick);
}
 
#include <wx/msgdlg.h>
 
void PolyLineTool::onClick(wxCommandEvent& event)
{
    wxMessageDialog(NULL,"test");
}
 

Note: les valeurs sont codées en dur pour l'instant, mais plus tard elles seront évidemment stockées dans un fichier de configuration. Pour le moment je veux juste un plug-in simple qui marche totalement...

Dernière modification par freem (06-03-2012 11:22:08)

10-03-2012 11:56:26  Re: AuiToolbarItem liés à des entrées de menu #4
Xaviou (Administrateur)
Lieu: Annecy (74)
Inscrit le : 27-08-2007
Messages: 1387
Snippets: 25
Tutoriels: 6
Site web
Hors ligne
Salut.

As-tu essayé de remplacer le "Bind" par un classique "Connect", pour voir ?

@+
Xav'

Le nouveau portail wxWidgets francophone : www.wxdev.fr
Ben en fait, vous y êtes déjà... et effectivement, depuis le temps, ce n'est plus tellement nouveau....
12-03-2012 22:19:02  Re: AuiToolbarItem liés à des entrées de menu #5
freem (Membre)
Inscrit le : 09-04-2010
Messages: 11
Snippets: 0
Tutoriels: 0
Hors ligne
J'ai refait une grosse partie du code, et du-coup, ce n'est plus juste que ça ne marche pas, mais il y a carrément un accès à un pointeur null.

Je n'ai pas essayé avec Connect, vu que, selon la doc, il n'est possible d'utiliser Connect que dans des conditions particulière, par exemple hériter d'une classe.
En fait, je dois reconnaître ne pas avoir super bien compris comment la gestion des évènements fonctionne...
Jusqu'ici j'utilisais un RAD qui générais du code que je ne maîtrisais pas vraiment, mais qui correspondait à mes besoins.
La, c'est plus pointu ce qui me force a chercher à comprendre la bête ^^

Par contre, je ne suis pas trop sûr que le problème vienne de la portion de code utilisant wxWidgets... Après quelques essais, je parviens a utiliser (plus ou moins) Bind en dehors de la classe héritière de wxFrame.
Le souci apparaît lorsque je tente d'utiliser le plugin, c'est alors la classe interface qui est utilisée, au lieu du véritable plug-in. Ce qui évidemment, empêche quelques initialisations, qui à leur tour déclenchent des exceptions...
J'ai comme l'impression que le framework qui me permet d'avoir un système de plugin n'est pas étranger à mes soucis... Je vais creuser de ce côté-ci.

Je mets un résolu ici vu que je soupçonne que la cause ne vienne pas de wxWidgets, ou j'attend d'avoir une solution réelle à poster?

Dernière modification par freem (12-03-2012 22:22:21)

13-03-2012 09:37:13  Re: AuiToolbarItem liés à des entrées de menu #6
Xaviou (Administrateur)
Lieu: Annecy (74)
Inscrit le : 27-08-2007
Messages: 1387
Snippets: 25
Tutoriels: 6
Site web
Hors ligne
freem:
Je n'ai pas essayé avec Connect, vu que, selon la doc, il n'est possible d'utiliser Connect que dans des conditions particulière, par exemple hériter d'une classe.
C'est la première fois que j'entends parler ce ça.
Pour ma part, je l'utilise quasiment tout le temps (vu que Bind n'existe pas sous wxWidgets-2.8 ) et sans forcément dériver les classes de bases.

freem:
Je mets un résolu ici vu que je soupçonne que la cause ne vienne pas de wxWidgets, ou j'attend d'avoir une solution réelle à poster?
C'est toi qui vois...

@+
Xav'

Le nouveau portail wxWidgets francophone : www.wxdev.fr
Ben en fait, vous y êtes déjà... et effectivement, depuis le temps, ce n'est plus tellement nouveau....
14-03-2012 11:28:27  Re: AuiToolbarItem liés à des entrées de menu #7
freem (Membre)
Inscrit le : 09-04-2010
Messages: 11
Snippets: 0
Tutoriels: 0
Hors ligne
Bon bah j'ai trouvé...
L'erreur ne venait pas de mon utilisation de wxWidgets (enfin, pas ce bug-ci) mais d'un problème de plug-in.

Je ne situe pas encore le problème avec exactitude, mais je l'ai "résolu" sur un projet "banc de test" en incluant directement le code dans le projet.
Du coup le but premier (création de menu si un élément manque, création d'une auitoolbar si nécessaire, et ajout de l'objet en question au menu et à la toolbar) fonctionne parfaitement, les évènements sont aussi gérés.

Voici mon code revu et corrigé (a part la licence que je vais peut-être mettre en LGPL pour ce code qui va intégrer une lib externe.) :

item.h:

Code Cpp:

 
/**********************************************************************************
*autorealm - A vectorized graphic editor to create maps, mostly for RPG games    *
*Copyright (C) 2012 Morel Bérenger                                               *
*                                                                                *
*This file is part of autorealm.                                                 *
*                                                                                *
*    autorealm 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.                                         *
*                                                                                *
*    autorealm 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 autorealm.  If not, see <http://www.gnu.org/licenses/>.          *
**********************************************************************************/

#ifndef ITEM_H
#define ITEM_H
 
#include <string>
#include <vector>
#include <map>
 
#include "menudata.h"
#include "../utils/utils.h"
 
class Container;
class AppConfig;
 
class Item
{
public:
protected:
private:
 
public:
    /** \brief Default Ctor
     *    assign an id to m_id and initialize m_callback to make it easy to detect lack of initialization
     */

    Item(void);
 
    /** \brief create controls to use a plug-in and bind it's action
     * The plug-in add it's controls to it's parent's lists.
     * The location of several things will be given by the AppConfig object
     *
     * \param parent wxFrame* container which will own the plug-in controls
     * \param containers std::map<std::string,Container>& list of parent's plug-in
     * \param appConfig AppConfig const& configuration of the application
     * \return void
     *
     */

    void registerIn(wxFrame *parent,std::map<std::string,Container>&,AppConfig const& appConfig);
 
    /** \brief enable the plug-ins controls
     *    \throw std::logic_error if the m_callback member was not initialized
     */

    void enable(void);
 
    /** \brief read the plug-in's configuration
     * \param graphicalResources std::string const& path where data will be searched
     */

    void readConfig(AppConfig const& config);
 
    /** \brief create the plug-in's menu item entry
     *    The menu path will be created if it does not exist
     *    \throw std::runtime_error if the path is empty (probably a configuration's problem)
     *    \throw std::runtime_error if the just created menu is not found (should never happen, but I prefer to check the most thing possible from wxWidgets)
     */

    void createMenu(void);
 
    /** \brief create the plug-in's toolbar item and bind it to the corresponding toolbar
     *    \note the toolbar is created if it does not exists
     *
     * \param containers std::map<std::string, Container>& list of existing toolbars
     */

    void createToolbarItem(std::map<std::string,Container>&containers);
 
protected:
    /** \brief follow the menu tree to find the last sub-menu corresponding with the plug-in path
     * \note this method is recursive
     * \param parent wxMenu* menu in which the next corresponding child will be searched
     * \param it std::vector<MenuData>::iterator& iterator to the plug-in's searched entry
     * \return wxMenu* found item
     */

    wxMenu* findLastMenu(wxMenu *parent,std::vector<MenuData>::iterator &it);
 
    /** \brief create the absent tree where to insert the menu item which will represent the plug-in
     * \note this method is recursive
     * \param parent wxMenu* menu in which the next corresponding child will be created
     * \param it std::vector<MenuData>::iterator& iterator to the plug-in's created entry
     * \return wxMenu* made item
     */

    wxMenu* createMenuPath(wxMenu *parent,std::vector<MenuData>::iterator &it);
 
private:
    /** \brief Dumb, Stupid, and Useless method made for testing, while the plug-in architecture does not work
     *    \todo Remove me
     */

    void DumbMethod(wxCommandEvent& event);
 
    /** \brief encapsulate the wxWidgets wxMenu* wxMenu::GetMenu(int) and add some error checking
     * \throw std::runtime_error if the menu found is null
     * \param id int id of the menu to retrieve
     * \return wxMenu* address of the retrieved menu
     */

    wxMenu *GetMenu(int id);
 
public:
protected:
    wxFrame * m_parent;
///common parameters
    MenuData m_entry;
    long m_id;
 
///menu parameters
    std::vector<MenuData> m_path;
 
///toolbar parameters
    std::string m_longDoc;
    wxObject *m_unused;
    void (Item::*m_callback)(wxCommandEvent&);
    wxBitmap m_disabled,m_enabled;
private:
};
 
#endif
 


item.cpp:

Code Cpp:

 
/**********************************************************************************
*autorealm - A vectorized graphic editor to create maps, mostly for RPG games    *
*Copyright (C) 2012 Morel Bérenger                                               *
*                                                                                *
*This file is part of autorealm.                                                 *
*                                                                                *
*    autorealm 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.                                         *
*                                                                                *
*    autorealm 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 autorealm.  If not, see <http://www.gnu.org/licenses/>.          *
**********************************************************************************/

#include "item.h"
 
#include "container.h"
 
#include <stdexcept>
 
#include <wx/aui/aui.h>
#include <wx/aui/auibar.h>
#include <wx/menu.h>
 
#include "../gui/appconfig.h"
#include "../gui/MainFrame.h"
 
Item::Item(void)
:m_id(wxNewId()),m_callback(NULL)
{
}
 
void Item::readConfig(AppConfig const& config)
{
 
//!\todo the plug-in should open the file itself
//!\note the plug-in should read it's specific data before asking to Item to read common data
    std::string filename("item.cfg"),tmp(config.m_configfiles+"/plugins/"+filename);
    FILE *input=0;
    input=fopen(tmp.c_str(),"r");
    if(!input)
        throw std::runtime_error("can not open the file "+std::string(config.m_configfiles+"/"+filename));//!\todo write config and retry to read configuration before leaving
 
//retrieve tool-bar item's informations
    m_longDoc=readline(input);
    std::string disabledPath(readline(input));
    std::string enabledPath(readline(input));
 
    //!\todo check for errors while opening bitmap files
 
    if(disabledPath.empty())
        m_disabled=wxNullBitmap;
    else
        m_disabled=wxImage(config.m_graphicalResources+disabledPath);
 
    if(enabledPath.empty())
        m_enabled=wxNullBitmap;
    else
        m_enabled=wxImage(config.m_graphicalResources+enabledPath);
 
    //m_enabled=enabledPath.empty()?wxNullBitmap:wxImage(enabledPath);
 
    //retrieve all path entries
    MenuData entry;
    while(!feof(input))
    {
        try
        {
            entry.readFromFile(input);
        }catch(std::runtime_error &e)
        {
            throw e;
        }
        m_path.push_back(entry);
    }
 
    //take the last entry to put it in m_entry, and remove it from the path
    m_entry=m_path.back();
    m_path.pop_back();
    if(m_path.empty())
        throw std::runtime_error("configuration file corrupted");
 
    m_callback=&Item::DumbMethod;
}
 
void Item::registerIn(wxFrame *parent,std::map<std::string,Container>&containers,AppConfig const& appConfig)
{
    m_parent=parent;
 
    readConfig(appConfig);
    createMenu();
    createToolbarItem(containers);
    enable();
}
 
void Item::createToolbarItem(std::map<std::string,Container>&containers)
{
    std::map<std::string,Container>::iterator it=containers.find(m_entry.name);
    if(it==containers.end())
    {
        //create & register container
        Container c;
        c.first=new wxAuiToolBar(m_parent);
        c.second=wxAuiPaneInfo().Name(m_path.rbegin()->name).ToolbarPane().Caption(m_path.rbegin()->name).Layer(10).Top().Gripper();
        containers[m_entry.name]=c;
    }
    //create the item
    containers[m_entry.name].first->AddTool(m_id, m_entry.name, m_enabled, m_disabled, m_entry.kind, m_entry.name, m_longDoc, m_unused);
 
    //insert the item inside the container
    wxAuiManager::GetManager(m_parent)->AddPane(containers[m_entry.name].first,containers[m_entry.name].second);
}
 
void Item::createMenu(void)
{
    wxMenuBar *menubar=m_parent->GetMenuBar();
    wxMenu *menuitem;
    std::vector<MenuData>::iterator it=m_path.begin();
    int id;
 
    if(m_path.empty())
        throw std::runtime_error("Empty path are not allowed");
 
    //retrieve the id of the first menu if existing
    id=menubar->FindMenu(it->name);
    if(wxNOT_FOUND!=id) // found it? Find the last corresponding child
    {
        menuitem=GetMenu(id);
        menuitem=findLastMenu(menuitem,it);
    }
    else // not found? Create it and get the new menu
    {
        menubar->Append(new wxMenu(),it->name);
        id=menubar->FindMenu(it->name);
        if(wxNOT_FOUND==id)
            throw std::runtime_error("Can not find the menu with FindMenu, but we just create it!");
        menuitem=GetMenu(id);
    }
    // now create all sub-menus
    menuitem=createMenuPath(menuitem,it);
    // finish by creating the menu item itself
    menuitem->Append(new wxMenuItem(menuitem,m_id,m_entry.name,m_entry.help,m_entry.kind));
}
 
wxMenu *Item::GetMenu(int id)
{
    wxMenu *menu=m_parent->GetMenuBar()->GetMenu(id);
    if(NULL==menu)
        throw std::runtime_error("FindMenu gives something but GetMenu failed!");
    return menu;
}
 
wxMenu* Item::findLastMenu(wxMenu *parent,std::vector<MenuData>::iterator &it)
{
    {
        wxMenuBar *menubar=m_parent->GetMenuBar();
        long id;
        id=menubar->FindMenu(it->name);
        if(wxNOT_FOUND==id)
            return parent;
        parent=GetMenu(id);
    }
    ++it;
    return findLastMenu(parent,it);
}
 
wxMenu *Item::createMenuPath(wxMenu *parent,std::vector<MenuData>::iterator &it)
{
    ++it;
    if(m_path.end()==it)
        return parent;
 
    wxMenu *newMenu=new wxMenu();
    parent->AppendSubMenu(newMenu,it->name);
    return createMenuPath(newMenu,it);
}
 
void Item::enable(void)
{
    if(!m_callback)
        throw std::logic_error("Unable to bind null callback");
    m_parent->Bind(wxEVT_COMMAND_MENU_SELECTED, m_callback, this, m_id);
}



A noter tout de même que je n'ai pas finie la partie qui permet de relire la configuration, donc pour le moment, il faut la coller en dur dans la classe fille. Idem pour l'initialisation du callback qui sera lié a l'appli via Bind.

C'est donc encore amené à évoluer (gestion de la configuration: sauvegarde, chargement et GUI pour modifier tout de façon centralisée), il reste quelques tests à faire tout de même, surtout en conditions réelles (notamment vérifier que l'ajout de plusieurs item ne crée pas de collisions), mais le coeur est présent et (semble) fonctionnel.

Et ma foi, plutôt pratique je trouve (en même temps, j'en suis l'auteur ^^ ): pas de répétition des chaînes de caractères des entrées de menu/toolbar, plus besoin de créer chaque menu un par un, insertion dans le menu et dans la toolbar, et plus besoin de gérer les ID non plus.

Dernière modification par freem (14-03-2012 11:33:38)

Menu forum (navigation):
Pages: 1  
 
Accueil » Accueil forums » Utilisation générale wxWidgets
» AuiToolbarItem liés à des entrées de menu