Visualizzazione post con etichetta rcp. Mostra tutti i post
Visualizzazione post con etichetta rcp. Mostra tutti i post

martedì 8 maggio 2012

Eclipse Day 2012

Il 4 Maggio 2012 si è svolto, a Firenza, l'Eclipse Day, una conferenza di un giorno interamente dedicata al panorama Eclipse. L'evento, organizzato da RCP Vision, si è rivelato veramente interessante e denso. Devo ammettere che era da tempo che non partecipavo ad una conferenza così interessante!



L'evento si è svolto tutto in lingua inglese, con un servizio di traduzione al volo effettuato da interpreti professionisti. La location, il palazzo congressi, era veramente eccezionale e il catering è stato eccellente. Anche gli speaker che si sono avvicendati sono stati tutti professionali e puntuali, permettendo uno svolgimento dell'evento senza intoppi.





Ho seguito con grande interesse il keynote di Mike Milinkovich che ha parlato di Open Source e di come Eclipse abbracci e contribuisca a questa filosofia. Veramente illuminante il talk del committer Tom Schindl che ha illustrato come funzionerà internamente la nuova piattaforma Eclipse e4 e come la dependency injection permetta la rapida risoluzione di alcuni fastidiosi bug. Molto accattivante anche la presentazione di Ralf Sternberg circa RAP, un altro progetto che mi interessa molto anche se non ho ancora avuto modo di speramentarlo con applicazioni reali. Questi sono anche gli speaker con i quali ho conversato maggiormente durante i vari coffe break e alla conclusione dell'evento, ma tutti gli altri sono stati di pari livello. Gli unici talk che mi hanno un po' deluso sono stati quelli del guru Lars Vogel (autore di innumerevoli tutorials), che seppur di alta qualità tecnica è stato piuttosto noioso, e quello di Silvia Mazzini che ha presentato l'interessante collaborazione Polarsys in un modo per me un po' nebuloso e troppo burocratico. Non mi ha stupito nemmeno il talk circa Microsoft Azure, non tanto perché di casa Microsoft, ma perché ritengo che vendere virtualizzazione a colpi di click senza far preoccupare lo sviluppatore/amministratore dei problemi di balancing e performance sia pura fantasia (ammetto che qui scatta la mia inclinazione da databasista).
Una cosa che ho apprezzato molto è che praticamente ogni speaker ha presentato delle demo live che hanno permesso di verificare sul campo quanto appena esposto così da potersi rendere conto dei tempi effettivi di implementazione di concetti e soluzioni.

mercoledì 20 aprile 2011

Eclipse RCP e il suo utilizzo da parte di IBM...

Nell'epoca delle applicazioni Web puo' sembrare strano che si continui ad investire su applicazioni Rich Client. Come sviluppatore Java per me il top delle tecnologie RCP e' Eclipse RCP, e IBM ha attualmente utilizzato la sua ben collaudata piattaforma di sviluppo RCP per la realizzazione della applicazione per la gestione del reparto vendite di IBM stessa. Quando si dice "eat your dog food"!

giovedì 2 dicembre 2010

RCP: tray icon

Una applicazione RCP può facilmente visualizzare una icona integrata nel vassoio di sistema (tray). A tale icona può poi essere associato un menu' contestuale e delle relative azioni.
Per aggiungere una icona tray ad una applicazione RCP i passi da seguire sono fondamentalmente:
  1. ottenere un oggetto Tray dopo aver aperto la finestra principale, quindi dall'interno dell'ApplicationWorkbenchWindowAdvisor;
  2. si deve creare un TrayItem per ogni icona da visualizzare (tipicamente una sola);
  3. associare ad ogni item un listener per l'evento MenuDetect (click destro/menu' contestuale) e creare nel modo abituale il menu con le relative azioni e contributi;
  4. associare ad ogni item un listener per l'evento DefautlSelection per minimizzare/massimizzare la finestra principale (o un'altra parte dell'applicazione) quando l'utente fa click (sinistro) sull'icona nel tray.
  5. Il codice seguente mostra come configurare il tray in modo semplice, associando al menu' contestuale le azioni About e Exit. Il metodo configureTray dovrebbe essere chiamato nel post-window-open.

   private void configureTray(){
    // get the workbench window
    final IWorkbenchWindow window = this.getWindowConfigurer().getWindow();
   
    // get the tray for the current running system
    Tray tray = window.getShell().getDisplay().getSystemTray();
   
    // the tray could be null if the system does not support it
    if( tray == null )
        return;
   
    // if here I've got a valid tray, create a tray item and configure it with an image
    TrayItem trayItem = new TrayItem( tray,  SWT.NONE );
    // set the icon
    ImageRegistry imgRegistry = Activator.getDefault().getImageRegistry();
    trayItem.setImage( imgRegistry.get( ... ) );
    // set the tooltip
    trayItem.setToolTipText("...");
   
   
    // add a menu listener on the tray element, so that it can
    // show the menu
    trayItem.addListener( SWT.MenuDetect, new Listener(){

        @Override
        public void handleEvent(Event event) {
        // create a new menu manager
        MenuManager manager = new MenuManager();
        // create the context menu
        Menu trayItemMenu = manager.createContextMenu( window.getShell() );
       
        // add entries to the menu
        manager.add( ActionFactory.ABOUT.create(window) );
        manager.add( ActionFactory.QUIT.create(window) );
       
        // the menu must be visible
        trayItemMenu.setVisible( true );       
        }
       
    });
   
   
   
    // add a listener to the tray icon, so that when the user double clicks
    // on the item the window is popped up again and made visible
    trayItem.addListener( SWT.DefaultSelection, new Listener(){

        @Override
        public void handleEvent(Event event) {
        Shell windowShell = window.getShell();
        if( ! windowShell.isVisible() )
            windowShell.setVisible(true);
       
        windowShell.setMinimized(false);
       
           
        }
       
    });
    }

giovedì 25 novembre 2010

RCP: aggiunta della log view ad una applicazione

Grazie alla modularità di Eclipse (e OSGi) è veramente semplice aggiungere una vista di "log" ad una applicazione RCP.
Come prima cosa occorre aggiungere alla applicazione la dipendenza del plugin org.eclipse.ui.views.log.
Successivamente occorre aggiungere il comando al proprio set di menu (o pulsanti o toolbar):
  • selezionare l'elemento dove aggiungere il comando (ad esempio il menu') e aggiungere un nuovo comando con click destro -> New -> Command
  • specificare le informazioni del comando con l'id rappresentato in figura
  •  infine aggiungere al comando un parametro (click destro, New -> Parameter) e specificare l'id della vista da aprire come riportato in figura
 A questo punto l'applicazione visualizzerà il nuovo comando e aprirà la vista dei log dell'applicazione. Il sistema di loggin può essere usato in ogni punto dell'applicazione con il seguente frammento di codice:

ILog logger = Activator.getDefault().getLog();
StringBuffer buffer = new StringBuffer( 100 );
//.....
IStatus status = new Status( IStatus.INFO, 
                             Activator.PLUGIN_ID, buffer.toString() );
logger.log(status);

Eclipse RCP Preferences: memorizzare informazioni di login in uno splash screen interattivo

Eclipse RCP mette a disposizione dello sviluppatore un sistema complesso di preferenze che possono essere usate per salvare, ad esempio, informazioni di login. Le preferenze di Eclipse possono essere di due tipi fondamentali:
  • in chiaro
  • cifrate
Le ultime vengono salvate con un meccanismo a keyring e i plugin *.security forniscono tutti gli strumenti necessari al loro utilizzo. Ad esempio org.eclipse.equinox.security.ui permette la memorizzazione nel portachiavi con tanto di dialog per la configurazione del portachiavi stesso. 
Si pone tuttavia un problema per l'utilizzo delle preferenze cifrate: il workspace deve esistere ed essere in esecuzione. Ciò significa che lo sviluppatore ha piu' lavoro da svolgere nel caso in cui le preferenze siano usate da applicazioni RCP headless o, come nel mio caso, da splash screen interattivi (che agiscono prima del workspace stesso).
Qui sotto riporto un esempio di utilizzo di un motore di salvataggio/caricamento dei dati dell'ultimo utente che ha effettuato il login nell'applicazione. In questo caso occorrerebbe salvare i dati in modo cifrato, ma essendo il mio un caso di splash screen interattivo, e trattandosi di dati comunque non fondamentali (poiché nella mia applicazione esistono altri meccanismi di sicurezza), ho deciso di usare per semplicità le preferenze in testo semplice. Da notare che il motore qui sotto proposto estrae ed inserisce i dati in un oggetto User che funge da wrapper per la business logic dietro all'utente stesso.


public class UserPreferencesStorage {

    private String PREFERENCES_USER_ROOT_NODE = "saved-user";
    private String PREFERENCES_USERNAME_NODE  = "username";
    private String PREFERENCES_PASSWORD_NODE  = "password";
    private String PREFERENCES_USER_PK_NODE   = "userpk";

 
    /**
     * Saves the preferences of the provided user.
     * @param user the user that must contain the password and the username to store
     * @return true if the storage has been done
     */
    public boolean saveUserPreferences( User user ) throws BackingStoreException{
 try{
     // get the normal preferences and the scope for this plugin
     IEclipsePreferences preferences = new ConfigurationScope().getNode( Activator.PLUGIN_ID );
     // now get the main node for the configuration storage
     Preferences savedPreferences = preferences.node(PREFERENCES_USER_ROOT_NODE);

     // now store the username and the password
     savedPreferences.put( PREFERENCES_USERNAME_NODE, user.getUsername() ); 
     savedPreferences.put( PREFERENCES_PASSWORD_NODE, user.getPassword() ); 
     savedPreferences.put( PREFERENCES_USER_PK_NODE, user.getPrimaryKey() + "" );  
     
     // flush the storage to ensure everything is written to disk
     preferences.flush();
     
     // all done
     return true;
     
 }catch( BackingStoreException se ){
     // log the exception and do nothing, cannot save
     IStatus status = new Status( IStatus.ERROR,     // severity
      Activator.PLUGIN_ID,      // the id of the application/plug-ing
      "Errore nel salvataggio delle preferenze di login utente",  // message
      se
     );

     throw se;
 }

    }
    
    
    /**
     * Loads a user from the prefences store.
     * @return the user loaded or null
     */
    public User loadUser() throws StorageException{
 // get the normal preferences and the scope for this plugin
 IEclipsePreferences preferences = new ConfigurationScope().getNode( Activator.PLUGIN_ID );
 // now get the main node for the configuration storage
 Preferences savedPreferences = preferences.node(PREFERENCES_USER_ROOT_NODE);
 
 // get the values from the storage
 String username  = savedPreferences.get(PREFERENCES_USERNAME_NODE, "");
 String password  = savedPreferences.get(PREFERENCES_PASSWORD_NODE, "");
 String primaryKey  = savedPreferences.get(PREFERENCES_USER_PK_NODE, "0");
 
 
 // create the user with such values
 User user = new User( Integer.parseInt(primaryKey), username, password );
 
 // all done
 return user;
    }
}

martedì 16 novembre 2010

SWT Virtual Table: problemi di sorting

Le tablle Virtual in SWT sono sicuramente un componente tanto potente quanto fragile, lo dimostrano due problemi che ho incontrato nello sviluppo di una applicazione che mi hanno fatto deistere dal continuare ad usarle, almeno per il momento. Il mio ovviamente si tratta di un caso particolare: non sempre è possibile rinunciare alle tablle virtual a favore di quelle normali!
Il primo problema riguarda l'update degli indici: sembra ci sia un bug che richiede l'update di indici fuori dallo schermo, con conseguente caricamento dei dati necessari, e si può facilmente intuire come in una applicazione database oriented (come la mia) questo sia un consumo eccessivo di risorse che non giustifica l'uso di SWT.VIRTUAL.
Il secondo problema riguarda il sorting effettuato tramite comparators: se la tabella viene gestita come Virtual (anche se in effetti viene usata come tabella normale), il sistema potrebbe passare un elemento null al metodo di comparazione del Comparator. Il problema non è tanto difendersi da un oggetto null in ingresso, cosa sicuramente banale, ma il fatto che una comparazione fra due entità di cui una null non può essere fatta, poiché questo impedisce la determinazione del corretto indice di inserimento nella table viewer.

mercoledì 10 novembre 2010

RCP: ottenere le informazioni di plugin a run-time (immagini e versione del prodotto)

Capita spesso di dover accedere a delle informazioni di definizione di un plugin RCP a run-time. Gli esempi piu' ricorrenti sono le immagini associate ad una applicazione RCP e i dati del bundle che definisce l'applicazione stessa (ad esempio il numero di versione). RCP mette a disposizione una serie di facilities per ottenere tali informazioni.
Il primo passo consiste nell'ottenere una istanza di IProduct, dal quale si può estrarre il defining bundle e da questo la lista delle immagini configurate nel file plugin.xml. Tale lista è in realtà una stringa unica con tutte le immagini alle varie dimensioni (16x16, 32x32, 48x48, ecc.) dalla quale occorre estrarre la lista effettiva dei percorsi (relativi al bundle) delle immagini. Avendo ottenuto la lista delle immagini è possibile inserirle nella shell corrente (ad esempio uno splash screen). Il seguente blocco di codice riassume i passi da fare:

        // get the product for the running instance
        IProduct product = Platform.getProduct();
       
        // get my shell
        Shell myShell = ...
       
        // the product can be null if the running instance is launched
        // as an eclipse application (e.g., during the development)
        if( product == null )
            return;
       
        // get the bundle symbolic name
        String bundleID = product.getDefiningBundle().getSymbolicName();
        // get the array of the application icons (defined thru the product)
        // the method will return a single string with the list of images
        // used at different sizes (16x16, 32x32, 48x48 and so on). The list is
        // comma separated, so I need to extract every single item.
        String imagePaths = product.getProperty( 
                                        IProductConstants.WINDOW_IMAGES );
        List imagePathList = new LinkedList();
        StringTokenizer commaTokenizer = new StringTokenizer(imagePaths, ",");
        while( commaTokenizer.hasMoreTokens() )
        imagePathList.add( commaTokenizer.nextToken() );
       
        // now that I've got the list of image paths (separated) 
        // I can create every
        // single image using an ImageDescriptor
        Image[] windowImages = new Image[ imagePathList.size() ];
        for( int i = 0; i < imagePathList.size(); i++ ){
           ImageDescriptor currentDescriptor =                    
               AbstractUIPlugin.imageDescriptorFromPlugin(    bundleID, 
                                                imagePathList.get(i) );
               windowImages[ i ] = currentDescriptor.createImage();
        }
       
        // now set the images for this window
        myShell.setImages( windowImages );




Analogamente, se si volessero ottenere informazioni circa la versione e il nome dell'applicativo si puo' usare il seguente pezzo di codice:


Bundle definingBundle = product.getDefiningBundle();
Label valueLabel = new Label(  definingBundle.getSymbolicName() );
Label versionLabel = new Label( definingBundle.getVersion().toString() );

o se si vuole cambiare il titolo della finestra principale del workspace affinché rifletta tali informazioni è sufficiente, nel metodo ApplicationWorkbenchWindowAdvisor.preWindowOpen() il seguente blocco di codice:

   // get the window configurer object
    IWorkbenchWindowConfigurer configurer = this.getWindowConfigurer();

   
   
    // set the title of the window
    StringBuffer title = new StringBuffer( 100 );
    title.append( "My App" );    // the fixed part of the title
   
    // get the information from the product
    IProduct product = Platform.getProduct();
    if( product == null )
        title.append( " [Eclipse Application - Development]" );
    else{
        title.append( " [RCP Product -" );
        title.append( product.getName() );
        title.append( " ~~ " );
       
        // get some information from the bundle
        Bundle definingBundle = product.getDefiningBundle();
        title.append(" ID: " );
        title.append( definingBundle.getSymbolicName() );
        title.append(" ~~ Ver.: " );
        title.append( definingBundle.getVersion() );
    }
   
    // set the real title
    configurer.setTitle( title.toString() );

mercoledì 30 giugno 2010

SWT Virtual Table, libpango e lo strano crash dell'applicazione...

Dopo un sacco di tempo speso nel debugging di una applicazione RCP, sono riuscito a scoprire la causa, abbastanza subdola, di uno strano crash che sembrava essere dovuto da libpango.
La mia applicazione utilizza una serie di tabelle SWT in modalità virtual, quindi con un rendering delle righe fatta su richiesta a seconda della visualizzazione del componente grafico. E qui entra in gioco libpango che si occupa della visualizzazione del testo in Gnome (e quindi su sistemi *nix). Ogni tanto ahimé capitava che l'applicazione andasse in crash con il blocco totale della JVM e uno stack libpango seguito da un dump completo della memoria.
Ebbene ecco la causa:
  1. essendo la tabella in modalità virtual, ogni volta che doveva disegnare una nuova riga si entra in uno stack nativo (SWT si appoggia ai componenti nativi grafici)
  2. la tabella richiama un handler Java per ottenere i dati da visualizzare
  3. nel mio caso l'handler utilizza JDBC/Spring per recuperare un dato da un database
  4. la connettività genera un'eccezione
  5. l'eccezione produce un errore nello stack nativo, che a sua volta produce un crash nella libreria libpango e quindi il blocco della JVM
Rimuovendo l'eccezione nell'handler Java (ovvero gestendo opportunamente gli errori) sono giunto alla eliminazione del crash. 
Ovviamente, come si evince dalla trattazione, scoprire questo problema non è stato per niente banale a causa dello stack nativo e del suo wrapping attorno al vero problema lato Java che si veniva a generare.

martedì 29 giugno 2010

Eclipse Product: interactive splash screen non visibile nel prodotto esportato

Dopo diverse prove e controprove sono finalmente riuscito a capire come mai un mio prodotto RCP non mostrava lo splash screen interattivo nella sua versione esportata mentre lo mostrava se lanciavo direttamente il prodotto dall'IDE. Il problema è dovuto all'assenza del nome del plugin che definisce lo splash screen, che nel mio caso coincideva con il plugin principale del prodotto stesso. In una simile condizione, l'avvio tramite IDE va a richiamare in automatico lo splash screen poiché trovato all'interno del plug-in stesso, mentre il prodotto finito non sa da quale plug-in prelevare lo splash screen e quindi non lo visualizza. Certo sarebbe molto piu' semplice uniformare il meccanismo di caricamento e risoluzione fra IDE e prodotto finito, o quantomeno inserire dei warning per simili sviste....

giovedì 8 aprile 2010

Echo char in un campo password

SWT utilizza un normale campo di testo (Text) per il prompt utente di una password. L'idea è simile a quella usata in AWT: si usa un campo testo con un echo char differente (ad esempio *) per impedire la lettura dei caratteri digitati.
Lo stile SWT.PASSWORD imposta un campo di testo con il carattere di echo della piattaforma, che corrisponde a 0x25cf su molte piattaforme. Di conseguenza, se si deve reimpostare il carattere di echo di un campo password (ad esempio perché è stato precedentemente impostato a '\0' per visualizzare i dati in esso contenuti) è sufficiente impostare il carattere di echo come segue:


char echoChar = 0x25cf;
passwordField.setEchoChar( echoChar );

mercoledì 7 aprile 2010

Modifiche invisibili a plugin.xml

Ho scoperto per caso che il file plugin.xml di una applicazione RCP non viene letto ad ogni avvio della stessa, ma solo ad ogni "avvio pulito". Di conseguenza, se è necessario apportare delle modifiche al file di una applicazione della quale è già stato fatto il deployment, occorre fare un riavvio dell'applicazione con l'opzione -clean al fine di ordinare alla piattaforma di rileggere le proprietà del file.

mercoledì 3 marzo 2010

Caricare una immagine dal bundle di una applicazione RCP

Il caricamento di una immagine contenuta in un bundle ma non nell'ImageRepository di una applicazione RCP non è un'operazione così banale come si potrebbe pensare, soprattutto se quello che serve non è tanto l'immagine ma i byte legati ad essa. In questo caso occorre infatti avere la possibilità di aprire uno stream verso l'immagine, e quindi si deve poter recuperare esattamente la posizione dell'immagine su disco.
Il primo passo è quello di ottenere il bundleimg corrente, e da esso ottenere l'URL dell'immagine con percorso assoluto "relativamente" al bundle stesso. In altre parole, se l'immagine si trova nella cartella del bundle, il percorso deve essere reso relativo ovvero deve diventare /img. L'URL così ottenuta è in una forma strana, come ad esempio:
bundleentry://33.fwk43086831/img/logo.png
Questo URL deve essere tradotto in una forma che abbia senso, ovvero in un percorso valido per il file system: questo viene fatto mediante il FileLocator. Ottenuto quindi l'URL effettivo (valido per il file system) dell'immagine, è possibile costruire un File e da esso un FileInputStream per ottenere i byte dell'immagine stessa.

In sostanza il codice risulta come il seguente:

Bundle bundle = Platform.getBundle( your.package.Activator.PLUGIN_ID );
URL url = bundle.getEntry(DEFAULT_LOGO_IMAGE_FILE_NAME);
URL fileURL = FileLocator.toFileURL( url );
File dummyImage = new File( fileURL.getPath() );
InputStream is = new FileInputStream(dummyImage);
byte[] bytes = new byte[ is.available() ];

is.read( bytes );

Problemi nell'esportazione di un prodotto RCP per Mac OS X

Come sottolineato in questo thread del forum ufficiale di RCP, l'esportazione di un prodotto per Mac OS X (Cocoa o Carbon) da una piattaforma di sviluppo Linux genera qualche problema. A dire il vero i problemi li hanno i Mac e il loro file system che non riconosce due file come distinti se il loro nome differisce solo per le maiuscole/minuscole. Comunque l'esportazione del prodotto RCP con i delta pack genera due directory .app all'interno della root finale (la cartella eclipse), una con l'iniziale del nome maiuscola e una con il nome tutto minuscolo. La soluzione sembra essere quella di eliminare la cartella con il nome maiuscolo e usare l'applicazione con il nome minuscolo.
Oppure si deve cambiare piattaforma di sviluppo e sperare in maggiore fortuna. Certo che dal team di Eclipse mi aspettavo fosse molto piu' semplice e immediato esportare un prodotto RCP per le varie piattaforme.

giovedì 18 febbraio 2010

About Dialog Command in una applicazione RCP

Per visualizzare la finestra di dialogo about dialog configurata all'interno di un prodotto Eclipse RCP è possibile usare il comando org.eclipse.ui.help.aboutAction nel seguente modo:


                  commandId="org.eclipse.ui.help.aboutAction"
                  icon="img/info.png"
                  id="hrpm.rcp.menu.aiuto.about"
                  label="Informazioni sull'applicazione"
                  style="push"
                  tooltip="Visualizza le informazioni di rilascio di questa applicazione">
            
         

lunedì 15 febbraio 2010

SWT.FULL_SELECTION: momenti di panico nella migrazione di un prodotto

Le tabelle SWT hanno un comportamento abbastanza curioso: di default su piattaforma Linux consentono la selezione di una tupla cliccando su uno qualunque degli elementi visualizzati, mentre su piattaforma Microsoft Windows no. Ho scoperto questo dopo aver migrato un prodotto da Linux a Windows: all'avvio del programma su Windows tutto sembrava regolare, fino a quando non mi sono accorto di essere incapace di selezionare qualunque tupla di una qualunque tabella. Dopo vari esperimenti, aggiornamenti di JVM, ri-esportazione del prodotto, clean dell'intero progetto, il panico ha iniziato a prendere il sopravvento. Poi per caso ho scoperto di riuscire a selezionare la prima cella di ogni riga, e che quello corrispondeva alla selezione dell'intera riga. Avendo ripreso lucidità ho fatto qualche indagine arrivando a scoprire che esiste un particolare flag di SWT, SWT.FULL_SELECTION, che abilita la selezione di una tupla di una tabella indipendentemente dalla cella sulla quale si effettua il click del mouse.
Comportamento abbastanza bizzarro!

giovedì 11 febbraio 2010

Delta pack per Eclipse 3.5.x

Eclipse RCP consente la creazione di applicazioni Java con un launcher nativo per un determinato sistema operativo. Tuttavia, anche nella sua versione Plugin-Development l'IDE Eclipse viene distribuito con il solo launcher per la propria piattaforma. Questo limita l'esportazione di un prodotto ad un solo sistema operativo, a meno che non si installi il Delta Pack con i launcher per gli altri sistemi operativi.
Nella versione di Eclipse 3.5.1 (e immagino in tutte le versioni del ramo 3.5) l'installazione del Delta Pack è differente dalle precedenti release. Anzitutto occorre scaricare il delta pack appropriato dal sito di Eclipse. Occorre fare attenzione ai numeri di versione, poiché se si scarica un delta pack errato (es. quello per la 3.5.0 quando si usa la 3.5.1) si otterranno strani (e incomprensibili) errori nell'esportazione del prodotto. In particolare è facile incappare in errori del tipo Cannot find plugin org.eclipse.equinox.launcher.
Il delta pack non va installato nella home di eclipse, può rimanere in un qualsiasi percorso del sistema operativo. Una volta scaricato e installato il delta pack, è sufficiente andare nelle preferenze di sistema, alla voce Plug-in Development e poi Target platform, selezionare la piattaforma attualmente in uso e cliccare su Edit.  Successivamente si deve selezionare Add e poi Directory e selezionare il percorso che contiene il delta pack estratto. Il risultato sarà il caricamento dei plugin con i launcher delle altre piattaforme (ispezionabili dal tab plugin contents) e il refresh della target platform selezionata.


Visibilità dei comandi nelle viste di Eclipse

Sto lavorando ad una applicazione RCP che include diverse viste master/slave in forma tabellare: alla selezione di una tupla su una vista master cambia l'output della vista slave. Ogni vista ha una serie di comandi che contribuiscono (sotto forma di pulsanti) alla UI e alcuni di essi, ad esempio modifica devono essere visibili solo quando esiste una selezione attiva nela vista stessa. Il problema è che usando i comandi di visibilità sulla sola selezione non nulla, si ottiene che quando si seleziona qualcosa in una qualunque vista, anche i comandi delle altre viste si attivano. Questo perché la selezione usata in una clausola with ragiona sull'intera pagina attiva, non sulla vista a cui è collegato il comando. Occorre allora forzare un test in parallelo anche sul tipo di componente attivo in quel momento, in modo da verificare che la selezione non sia nulla e il focus sia su una determinata vista. Si deve quindi mettere in AND logico il test sulla proprietà activePartId che deve essere uguale all'identificativo RCP della vista stessa.
Maggiori dettagli in questo thread del forum Eclipse.

mercoledì 16 dicembre 2009

Un ComboBox per selezionare le viste di una applicazione RCP

In una applicazione RCP che sto sviluppando mi è capitato di avere molte viste, ciascuna che deve essere associabile ad una serie di permessi. Per permettere ad un utente amministratore di selezionare a quali viste concedere quali permessi, ho creato un semplice wrapper attorno ad un combo che contiene l'array delle viste disponibili e la loro descrizione. Il codice è abbastanza banale, ma potrebbe tornare utile in altre applicazioni.

public class ViewsCombo {

/**
* The combo that will be displayed.
*/
private Combo combo = null;


/**
* The available views in the system.
*/
private IViewDescriptor[] availableViews = null;

/**
* Initializes the combo and the map to contain the views.
* @param parent
*/
public ViewsCombo( Composite parent, String viewIDToSelect ){
super();
// create the combo
this.combo = new Combo( parent, SWT.READ_ONLY );

// fill the combo with the data
this.fillComboAndMap();

// select a view if specified
if( viewIDToSelect != null )
for( int i = 0; i < availableviews =" (IViewDescriptor[])">

venerdì 27 novembre 2009

SWT e class inheritance

I widget SWT non consentono l'eredità: al loro interno esiste un metodo (checkSubclass()) che effettua dei controlli per garantire che una sottoclasse non venga utilizzata in una applicazione.
Ovviamente questa è una restrizione a run-time, di fatto i componenti possono essere derivati da altri anche se poi non risulteranno utilizzabili a run-time.
Le motivazioni di questa scelta progettuale risiedono nella preferenza della composizione rispetto all'ereditarietà, e mi fa molto piacere vedere che esistano dei framework che si spingono in questa direzione.
Tuttavia ogni tanto è piu' comodo derivare widget da altri piuttosto che usare la composizione (che costringe l'utilizzo di interfacce e la costruzione di API wrapper), ma questo dovrebbe avvenire solo per widget per cui sia garantita la portabilità. Se non si vuole poi implementare la propria logica di controllo nel metodo checkSubclass() è necessario inserire i widgets in un package org.eclipse.swt.widgets.

SWT: Combo box con modello integrato

Il widget Combo della libreria SWT non consente l'inserimento nel drop down menu' di oggetti qualunque, ma solo di String. Questa è ovviamente una limitazione perché richiede che il combobox sia affiancato da un modello dati che consenta la correlazione fra indice selezionato e oggetto a cui ci si riferisce.
Creare un wrapper per il Combo che contenga un semplice modello dati lineare (monodimensionale) è un'operazione quasi banale. Nell'esempio sottostante si suppone che BusinessObject sia il capostipite degli oggetti che si vogliono selezionare tramite il Combo; questo wrapper rende il comportamento di Combo piu' simile al suo duale Swing JComboBox.

public class BusinessObjectCombo extends Combo {

public BusinessObjectCombo(Composite parent, int style) {
super(parent, style);
}

/**
* An internal list to keep track of all elements this combo is displaying.
*/
private List model = new LinkedList();


/**
* Provides the business object corresponding to the selected element in the combo.
* @return the business object from the combo
*/
public synchronized final BusinessObject getSelectedBusinessObject(){
// get the selected index
int index = this.getSelectionIndex();
// return the element from the model
return this.model.get(index);
}

/**
* Adds a new business object to the combo and to the internal model.
* @param newElement
*/
public synchronized final void add(BusinessObject newElement){
// check arguments
if( newElement == null )
return;
else{
// add the element to the list and to the combo
this.model.add(newElement);
this.add( newElement.toString() );
}
}

/**
* Removes the specified element from the combo.
* @param element
*/
public synchronized final void remove(BusinessObject element){
// get the index from the model
int index = this.model.indexOf(element);
if( index >= 0 ){
this.model.remove(index);
this.remove(index);
}

}


/**
* Clears all the elements from the combo and the internal model.
*/
public synchronized final void clear(){
this.model = new LinkedList();
this.removeAll();
}


/**
* Adds all the elements from the specified collection.
* @param businessObjects
*/
public synchronized final void addAll(Collection businessObjects ){
for( BusinessObject bo : businessObjects )
this.add( bo );
}

}