PDA

View Full Version : [JAVA] Component Top-Level


Gio Games
19-06-2010, 11:23
Salve a tutti,
sto cercando di capire come ottenere il Component che è "realmente" visible. Mi spiego meglio:
se per esempio ho due JWindow, entrambe a fullscreen, avrò la prima (che chiamo w1) che sarà nascosta dalla seconda (che chiamo w2).

Ora però non riesco a capire se esista un modo per determinare quale component è "localizzato" per esempio in un punto (x,y).

Vedo che la SwingUtilities ha un metodo (getDeepestComponentAt) che permette di ottenere il component localizzato dalle coordinate specificate, ma sempre relativamente a un "padre".

Vorrei capire come utilizzare quel metodo (o chi per lui :D ) su tutto lo schermo, così da ottenere il componente ad una determinata posizione.

Probabilmente ho fatto tanta confusione in questo post, spero che si capisca qualcosa. Se non fosse così sono felice di chiarirmi.

Grazie a tutti per le eventuali risposte

banryu79
21-06-2010, 09:30
se per esempio ho due JWindow, entrambe a fullscreen, avrò la prima (che chiamo w1) che sarà nascosta dalla seconda (che chiamo w2).

Ora però non riesco a capire se esista un modo per determinare quale component è "localizzato" per esempio in un punto (x,y).

Vedo che la SwingUtilities ha un metodo (getDeepestComponentAt) che permette di ottenere il component localizzato dalle coordinate specificate, ma sempre relativamente a un "padre".

Devi prima ottenere quel "padre".
Prova a consultare i javadoc di java.awt.KeyboardFocusManager.getGlobalActiveWindow() oppure java.awt.KeyboardFocusManager.getGlobalFocusedWindow() ;)

Gio Games
21-06-2010, 09:50
Intanto grazie mille per la risposta, so che la mia domanda è un tantino strana :D .

Guardando la javadoc però ho notato che entrambi i metodi sono protetti, quindi non potrei invocarli direttamente, sbaglio?

Cerco di spiegarti meglio la mia situazione:
sto cercando di creare un piccolo dispatcher per la gestione di eventi touch, derivanti da uno schermo creato con la tecnologia FTIR, quindi l'event dispatcher di java non credo riesca ad aiutarmi. In realtà il piccolo dispatcher l'ho già scritto e funziona su una gerarchia di componenti, ma per esempio se mostrassi a tutto schermo una nuova finestra e non la registrassi al dispatcher, il dispatcher non sapendo nulla di quest'ultimo componente continuerebbe a notificare eventi al layer sottostante.

Forse così si capisce un pò meglio il mio intento.

Grazie ancora per la risposta

banryu79
22-06-2010, 09:31
Ok, cerco di aiutarti ma non sono un esperto.
Basandomi sulla documentazione del JDK, in particolare leggendo i javadoc della classe java.awt.KeyboardFocusManager, vedo che quest'ultima espone dei metodi che permettono al chiamante di ottenere un riferimento alla Window "attiva" corrente, che, in un sistema, può essere solo una in un dato momento (la finestra che, direttamente o indirettamente possiede il "keyboard focus" in un dato momento).

Ho messo insieme un breve esempio che mostra il concetto (l'applicazione mostra due finestre a video: un frame e una dialog. La classe KeyboardFocusManagerUsage è un WindowFocusListener che viene registrato su entrambe le window e appoggiandosi al KeyboardFocusManager, ad ogni evento di "focus gained" mostra al centro dello schermo la finestra attiva e l'altra al lato sinistro o destro dello schermo. Spostando il focus su una o l'altra si verifica il comportamento descritto):

import java.awt.Dimension;
import java.awt.Frame;
import java.awt.KeyboardFocusManager;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

/**
* Build and display two frames.
* Tells which one is the "active Window" using the current java.awt.KeyborardFocusManager.
*
* @author francesco
*/
public class KeyboardFocusManagerUsage implements WindowFocusListener
{
private JFrame frame;
private JDialog dialog;
private KeyboardFocusManager currentKFM;
private static Dimension SCREEN;

public static void main(String... args) {
SCREEN = Toolkit.getDefaultToolkit().getScreenSize();
KeyboardFocusManagerUsage k = new KeyboardFocusManagerUsage();
k.runExample();
}

// frame & dialog setup
public KeyboardFocusManagerUsage() {
int x = 0, y = 0;
int width = 250, height = 300;

frame = new JFrame("Window [1] - a JFrame");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setBounds(x, y, width, height);
frame.addWindowFocusListener(this);

dialog = new JDialog((Frame)null, "Window [2] - a JDialog");
dialog.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
dialog.setBounds(x+width+10, y, width, height);
dialog.addWindowFocusListener(this);
}

private void runExample() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
dialog.setVisible(true);
currentKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager();
}
});
}


///// WINDOW FOCUS LISTENER IMPLEMENTATION

@Override
public void windowGainedFocus(WindowEvent e) {
Window active = currentKFM.getActiveWindow();
Window inactive = active.equals(frame) ? dialog : frame;

// center on screen "active" window
active.setLocationRelativeTo(null);

// show to the left (or right) of the screen the "inactive" window
int x = 0, y = 0;
if (inactive.equals(dialog)) {
x = SCREEN.width - inactive.getWidth();
}
inactive.setLocation(x, y);
}

@Override
public void windowLostFocus(WindowEvent e) {
}
}


Non so se per quello che vuoi fare però ti è sufficiente appoggiarti al KeyboardFocusManager...
Mi spiego: se non ho capito male, tu stai lavorando su una tecnologia FTIR, fino a 20 min fa non sapevo cos'era, cos'ì ho consultato il www.
E' sta roba qua?
link: http://www.instructables.com/id/Multitouch-Display-FTIR/step1/How-does-it-work-/#

Se sì, per caso il tuo intento è quello di catturare ogni singolo "touch" utente sul monitor FTIR e in qulche modo "reindirizzarlo" come evento del click del mouse sul sistema sottostante? In questo caso forse potrebbe esserti di aiuto la classe java.awt.Robot?

Gio Games
23-06-2010, 08:38
Grazie ancora per la cordialità.

Cercherò di capire se riesco in qualche modo ad adattare la tua soluzione al mio problema, ma credo di no perchè in pratica io ho un hashtable in cui per ogni Component ho registrato un DLMListener (che è un ascoltatore che ogni componente che vuole essere notificato di eventi touch deve implementare). Quindi il fatto di dover aggiungere un WindowFocusListener mi sembra dia dei grattacapi a livello di dispatcher.

Per quanto riguarda l'abbinamento touch-mouse ci avevo pensato, ma poi in quel modo non sarei riuscito a gestire eventi di tipo multitouch, come per esempio uno zoom con due dita, mentre in questo modo viene tutto molto naturale a livello applicazione: per far un esempio basta che un O2Component (ho dovuto estendere la gerarchia dei Component Java per aggiungere i metodi addDLMListener() e removeDLMListener()) aggiunga un ascoltatore DLM così che al momento di eventi su di lui questi vengano richiamati nella stessa maniera di eventi di tipo MouseListener, con la differenza che qui ci sono eventi di tipo Press, Click, Release, Move, Zoom.

Funziona tutto molto bene (a dir la verità meglio di quello che speravo :P), mi sono accorto solo ora del problema di finestre sovrapposte: intanto cerco di capire se posso utilizzare in qualche modo la tua soluzione.

Ti ringrazio ancora per tutta la disponibilità (e complimenti per tutte le tue conoscenze :) )

banryu79
23-06-2010, 11:54
Rettifico quanto ti ho detto (forse ora ho capito meglio cosa devi fare).

La classe java.awt.Container ha un metodo che fa al caso tuo: findComponentAt(int x, int y) che restituisce un riferimento al:

the visible child component that contains the specified position

a cui puoi quindi passare il tuo evento.

Questo significa che ogni volta che viene generato un evento touch devi decidere, nel tuo dispatcher, a quale componente inviarlo.
Risolverebbe il problema delle finestre che si sovvrapongono. Ovviamente il presupposto è che tu possa mettere le mani sulla lista dei tutte le finestre visibili a video in un dato momento.

@EDIT:
Un esempio per verificare da un'applicazione Java, quali sono le sue finestre visibili in un dato momento:

import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class FindAllFrames extends WindowAdapter
{
public static void main(String[] args) {
FindAllFrames faf = new FindAllFrames();

try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {}

System.out.println("VISIBLE APPLICATION WINDOWS");
Window[] windows = Window.getWindows();
for (Window w : windows)
if (w.isVisible())
System.out.println(w.getName()+": "+w.getClass());
}

public FindAllFrames() {
Dimension d = new Dimension(100, 250);

final JDialog d1 = new JDialog((JFrame) null, "Dialog 1");
d1.setSize(d);
d1.setName("Dialog 1");
d1.setTitle("Dialog 1");
d1.addWindowListener(this);

final JDialog d2 = new JDialog((Window) null, "Dialog 2");
d2.setSize(d);
d2.setName("Dialog 2");
d2.setTitle("Dialog 2");
d2.addWindowListener(this);

final JFrame f = new JFrame();
f.setSize(d);
f.setName("JFrame 1");
f.setTitle("JFrame 1");
f.addWindowListener(this);

final Window w1 = new Window(f);
w1.setSize(d);
w1.setName("Window 1");
w1.addWindowListener(this);

final Window w2 = new Window(null);
w2.setSize(d);
w2.setName("Window 2");
w2.addWindowListener(this);

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
d1.pack(); d1.setVisible(true);
d2.pack(); d2.setVisible(true);
f.pack(); f.setVisible(true);
//w1.pack(); w1.setVisible(true);
//w2.pack(); w2.setVisible(true);
}
});
}

@Override
public void windowClosing(WindowEvent e) {
((Window)e.getSource()).dispose();
}
}

Una Window è un Component, quindi puoi invocare findComponentAt(x, y)...
Per convertire le coordinate da/a schermo/componente puoi usare i due metodi che trovi in SwingUtilities.

Gio Games
23-06-2010, 14:21
Grazie per il suggerimento, per risolvere il problema del componente in posizione x,y avevo scritto un metodo ricorsivo che utilizza getComponentAt, quindi un pò di lavoro per niente :D. Ora dal dispatcher posso utilizzare i tuoi suggerimenti per rendere il tutto più pulito.

Però mi sembra che il problema fondamentale rimanga, perchè (se non mi sbaglio) in questo modo ottengo le Window che sono visibili a schermo, ma se la Window w1 viene scavalcata da una nuova Window w2 entrambe ritornano true da isVisible(), ma solo w2 è effettivamente visibile. Mi sbaglio?

banryu79
23-06-2010, 17:15
Però mi sembra che il problema fondamentale rimanga, perchè (se non mi sbaglio) in questo modo ottengo le Window che sono visibili a schermo, ma se la Window w1 viene scavalcata da una nuova Window w2 entrambe ritornano true da isVisible(), ma solo w2 è effettivamente visibile. Mi sbaglio?

No è corretto, hai perfettamente ragione.
Anche recuperando tutte le Window dell'applicazione, e considerando solo quelle che:
restituiscono true come valore di ritorno della chiamata Window.isShowing();
e che:
restiuiscono true come valore di ritorno della chiamata Window.contains(p) dove 'p' è il punto dello schermo da testare convertito in coordinate locali al sitema di riferimento della Window (SwingUtilities.convertPointFromScreen(p, w) -'w' è la Window-);

Il codice di quanto sopra sarebbe una cosa tipo:

import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Window;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class FindAllFrames
{
public static void main(String[] args) {
FindAllFrames faf = new FindAllFrames();

try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {}

System.out.println("VISIBLE APPLICATION WINDOWS");
Window[] windows = Window.getWindows();
for (Window w : windows)
if (w.isShowing())
System.out.println(w.getName()+": "+w.getClass());

try {
Thread.sleep(1000);
} catch (InterruptedException ignored) {}

// find component @ screen 0,0 (should be Frame 1)
// find component @ screen 150,25 (should be Dialog 1)
// find component @ screen 150,100 (should be Frame 1)
// find component @ screen 1000,1000 (no component at that location)
int[] coords = {0,0, 150,25, 150,100, 1000,1000};
for (int i=0; i<coords.length-1; i=i+2) {
int x = coords[i];
int y = coords[i+1];
System.out.println("@ X"+x+" Y"+y);
windows = findApplicationWindowsAtScreen(new Point(x, y));
for (Window w : windows)
System.out.println(w.getName());
}
}

public static Window[] findApplicationWindowsAtScreen(final Point screenPoint) {
Window[] windows = Window.getWindows();

List<Window> validWindows = new ArrayList<Window>();
for (Window w : windows)
if (meetRequirements(w, screenPoint))
validWindows.add(w);

if (validWindows.size() > 0) {
Window[] arr = new Window[validWindows.size()];
return validWindows.toArray(arr);
}
else
return new Window[0];
}

protected static boolean meetRequirements(Window w, final Point POINT) {
if (w.isShowing()) {
Point p = (Point) POINT.clone();
SwingUtilities.convertPointFromScreen(p, w);
return w.contains(p);
}
return false;
}

public FindAllFrames() {
Rectangle rf1 = new Rectangle(300, 300);
Rectangle rd1 = new Rectangle(100, 0, 200, 200);
Rectangle rf2 = new Rectangle(50, 50, 180, 150);

final JFrame f1 = new JFrame("Frame 1");
f1.setName("Frame 1");
f1.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
f1.setBounds(rf1);

final JFrame f2 = new JFrame("JFrame 2");
f2.setName("JFrame 2");
f2.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f2.setBounds(rf2);

final JDialog d1 = new JDialog((JFrame)null, "Dialog 1");
d1.setName("Dialog 1");
d1.setTitle("Dialog 1");
d1.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
d1.setBounds(rd1);

SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
f1.setVisible(true);
f2.setVisible(true);
d1.setVisible(true);
}
});
}
}

Purtroppo resta il fatto che può esserci contemporaneamente più di una finestra che risponde a questi requisiti, in un dato momento.

Pare che non ci sia diretto supporto allo Z-ordering delle top-level windows in AWT (a parte un paio di metodi tipo toFront() e toBack()), probabilmente perchè lo Z-Ordering è gestito in maniera molto specifica dal windowing system sottostante e AWT, essendo Java, mira ad essere multi piattaforma.

Comunque se stai lavorando su una tua singola applicazione potresti sfruttare la relativamente recente aggiunta in AWT/Swing del supporto per le translucent and transparent windows: la tua applicazione crea una completamente trasparente a tutto schermo che ti serve solo per raccogliere tutti gli eventi che ti arrivano e farne il dispatch ai componenti child (potresti usare JDesktopPane e JInternalFrame).
Eccoti due link per farti un'idea di ciò che permette:
- http://java.sun.com/docs/books/tutorial/uiswing/misc/trans_shaped_windows.html
- http://java.sun.com/developer/technicalArticles/GUI/translucent_shaped_windows/

Gio Games
24-06-2010, 13:55
Come temevo purtroppo. In realtà il programma era creare un framework che potesse prendere il posto dell'EDT Java, ma mi sa che sarà piuttosto impossibile :D .
Quindi non è una vera e propria applicazione, fin'ora funziona bene con le applicazioni che abbiamo creato, ma il limite sarà dunque il non-multitasking e il fatto che tutte le finestre debbano venire agganciate a una root.

Nulla di sconvolgente per carità, si riesce ad utilizzare molto meglio di quello che speravo, è che ormai stava salendo il desiderio di qualcosa di più universale.

Sei stavo veramente gentile ed utile, complimenti ancora per tutte le tue conoscenze e la tua disponibilità.

banryu79
24-06-2010, 14:58
Comunque sia immagino che tu sia già a conoscenza di realtà/librerie/framework tipo MT4j (http://www.mt4j.org/mediawiki/index.php/Main_Page), vero?

Puoi confermarmi una cosa (per mia curiosità e perchè magari posso indirettamente indirizzarti, forse)?
Da quello che stai cercando di fare si direbbe che a te gli eventi multitouch non vengono inviati direttamente dal SO sottostante (mediante i driver di cui sarà dotato per il dispositivo multitouch FTIR che usi) ma sei tu che ti sei fatto un dispositivo FTIR e l'input è il risultato ricavato dal processing dei frame della webcam che filma il monito FTIR con un filtro per gli infrarossi (e questo già da un contesto Java... Che so, magari stai usando JMF per interfacciarti conla webcam)?

E' così?
Comunque ciao e buon lavoro :)

Gio Games
24-06-2010, 16:59
Hai indovinato su tutto, tranne sul fatto che non conosco MT4j :eek:

Per cercare di far funzionar il framework ugualmente con questa piccola applicazione ho pensato che le nuove JWindow che creo le posso agganciare alla root. Ma in questo modo ho proprio un bel problemino.

In pratica quando clicco su un bottone sulla finestra di root, instanzio una nuova classe che estende una JWindow e l'aggancio alla root quindi setVisible(true).

Purtroppo in questo modo, a volte il bottone che ho schiacciato per aprire la nuova Jwindow rimane visualizzato anche se non è possibile scatenarci eventi. Credendo che fosse un problema di concorrenza ho messo il setVisible(True) all'interno di uno costrutto di questo tipo:

EventQueue.invokeLater(new Runnable() {
public void run() {
setVisible(true);
}
});

ma senza miglioramenti. Sembra come se la schermata contenga il rilascio del bottone del mouse.

Sbaglio qualcosa con l'invokeLater?

banryu79
24-06-2010, 17:40
Hai indovinato su tutto, tranne sul fatto che non conosco MT4j :eek:

Merito di google, a volte spendere del tempo per fare ricerche mirate sul web può essere molto utile ;)

Per il resto di quello che hai scritto, non ho capito bene la situazion, dovresti provare a spiegare le cose e il contesto con più chiarezza.
L'uso che fai di invokeLater è apparentemente corretto, sarebbe però utile postassi il resto del codice (della classe) in cui è contenuto.

Al di là di questo, mi par di capire che cerchi di istanziare eventuali finestre come finestre-figlie di 'root' che è la finestra top level della tua applicazione, in modo da poter sfruttare il LayeredPane e gestire lo z-order esplicitamente (il che ti risolverebbe l'enpasse relativa alle finestre che si sovrappongono, di cui sopra) per lo smistamento corretto degli eventi nel tuo dispatcher.

Se questo è il tuo intento, perchè non utilizzi JDesktopPane come 'root' e istanzi JInternalFrame come finestre child?
Farebbe proprio al caso tuo (gestiresti esplicitamente lo z-order degli internal frame tramite la loro posizione nel layered pane del JDesktopPane che fa da 'root container' di tutte le finestre...)
Qui delle info (http://java.sun.com/docs/books/tutorial/uiswing/components/internalframe.html), così puoi valutare se per i tuoi scopi possa andare bene come soluzione.

Poi a scanso di equivoci, ripeto: non sono un esperto.
Ti augro che passi per questo lido qualcuno di veramente ferrato che magari sa metterti sulla giusta strada in due parole ;)

Gio Games
24-06-2010, 17:52
Ti ringrazio tantissimo del tuo aiuto, se non sei un esperto non oso immaginare gli esperti :D...mi sento veramente una nullità a confronto tuo ahahaha.

Comunque utilizzo delle JWindow perchè vorrei che l'applicazione sia fullscreen come i relativi figli.

Comunque posto il codice della classe principale (che in pratica è una trayIcon, TrayStickyNotes), di una classe che rappresenta la finestra di root (Houseboard) e di una classe che viene richiamata (VKeyboard):


package houseboard;

import DLMListener.Calibration;
import java.awt.Image;
import java.awt.MenuItem;
import java.awt.PopupMenu;
import java.awt.SystemTray;
import java.awt.TrayIcon;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;

/**
*
* @author gioele
*/
public class TrayStickyNotes
{
public static final String DEFAULT_TOOLTIP = "StickyNotes: nessuna nuova nota";
private static boolean calibrate = false;
private static Houseboard bacheca;
private static TrayIcon trayIcon = null;
private static boolean touchMode;

public TrayStickyNotes(boolean touchMode)
{
setOutputStream();

TrayStickyNotes.touchMode = touchMode;
try
{
Image image = ImageIO.read(new File("images/trayIcon.png"));

PopupMenu menu = new PopupMenu();
MenuItem exit = new MenuItem("Esci");

exit.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
bacheca.close();
}
});

menu.add(exit);

trayIcon = new TrayIcon(image, DEFAULT_TOOLTIP, menu);
trayIcon.setImageAutoSize(true);
trayIcon.addMouseListener(new TrayListener());

SystemTray.getSystemTray().add(trayIcon);

bacheca = new Houseboard();
}
catch (Exception ex)
{
bacheca = new Houseboard();
if(touchMode)
new Calibration(bacheca);
else
bacheca.runApp();
}

}

private void setOutputStream()
{
OutputStream output = null;
try
{
output = new FileOutputStream("log.txt");
PrintStream printOut = new PrintStream(output);
System.setOut(printOut);
}
catch (FileNotFoundException ex)
{
System.out.println("Impossibile redirezionare l'output su log.txt: " + ex);
}
}

public static void newMsg(String msg)
{
if(!bacheca.isVisible())
{
trayIcon.displayMessage(null, msg, TrayIcon.MessageType.INFO);
trayIcon.setToolTip(msg);
}
}

public static void setText(String msg)
{
trayIcon.setToolTip(msg);
}

public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
/* if(args.length > 0)
{
if(args[0].equals("-touch"))
new TrayStickyNotes(true);
else
new TrayStickyNotes(false);
}
else*/
new TrayStickyNotes(false);
}
});
}

private class TrayListener implements MouseListener
{
public void mouseClicked(MouseEvent e)
{
if(e.getButton() == MouseEvent.BUTTON1)
{
if(!calibrate && touchMode)
{
new Calibration(bacheca);
calibrate = true;
}
else
{
bacheca.showApp();
}
}

trayIcon.setToolTip(DEFAULT_TOOLTIP);
}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}
}
}


Quindi Houseboard

package houseboard;

import OxygenGraphics.*;
import DLMListener.*;
import MoveNClick.*;
import houseboard.Users.*;
import houseboard.StickyNote.*;
import houseboard.StickyNote.StickyNote.GenericStickyNote;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Vector;

/**
*
* @author gioele
*/

public class Houseboard extends O2Window implements MouseListener, DLMListener, LoginListener, DLMApplet
{
private static final int BUTTON_WIDTH = 200;
private static final int BUTTON_HEIGHT = 30;

private O2Button loginButton,
addNoteButton,
addAnswerButton,
addExtendedAnswerButton,
addAlarmNoteButton,
addUserButton,
addAdminButton,
deleteUserButton,
atTrayButton,
exitButton;
private CorkPanel corkArea;

private Vector<StickyNoteWidget> widgets;

public Houseboard()
{
int screenX = Toolkit.getDefaultToolkit().getScreenSize().width;
int screenY = Toolkit.getDefaultToolkit().getScreenSize().height;

// impostiamo le dimensioni della finestra al massimo possibile
setSize(screenX, screenY);

// aggiungiamo lo sfondo di sughero
corkArea = new CorkPanel();
corkArea.setLayout(null);
corkArea.setSize(this.getWidth(), this.getHeight());
corkArea.setLocation(0, 0);

this.setContentPane(corkArea);

// aggiungiamo i pulsanti coi relativi ascoltatori
loginButton = new O2Button("Login");
loginButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
loginButton.setLocation(0, 0);
loginButton.addMouseListener(this);
loginButton.addDLMListener(this);
corkArea.add(loginButton);

addNoteButton = new O2Button("Add StickyNote");
addNoteButton.setVisible(false);
addNoteButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addNoteButton.setLocation(0, screenY - addNoteButton.getHeight());
addNoteButton.addMouseListener(this);
addNoteButton.addDLMListener(this);
corkArea.add(addNoteButton);

addAnswerButton = new O2Button("Add Answer");
addAnswerButton.setVisible(false);
addAnswerButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addAnswerButton.setLocation(addNoteButton.getX() + addNoteButton.getWidth(), addNoteButton.getY());
addAnswerButton.addMouseListener(this);
addAnswerButton.addDLMListener(this);
corkArea.add(addAnswerButton);

addExtendedAnswerButton = new O2Button("Add Extended Answer");
addExtendedAnswerButton.setVisible(false);
addExtendedAnswerButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addExtendedAnswerButton.setLocation(addAnswerButton.getX() + addAnswerButton.getWidth(), addAnswerButton.getY());
addExtendedAnswerButton.addMouseListener(this);
addExtendedAnswerButton.addDLMListener(this);
corkArea.add(addExtendedAnswerButton);

addAlarmNoteButton = new O2Button("Add Alarm Note");
addAlarmNoteButton.setVisible(false);
addAlarmNoteButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addAlarmNoteButton.setLocation(addExtendedAnswerButton.getX() + addExtendedAnswerButton.getWidth(), addExtendedAnswerButton.getY());
addAlarmNoteButton.addMouseListener(this);
addAlarmNoteButton.addDLMListener(this);
corkArea.add(addAlarmNoteButton);

addUserButton = new O2Button("Add User");
addUserButton.setVisible(false);
addUserButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addUserButton.setLocation(screenX - addUserButton.getWidth(), addAnswerButton.getY());
addUserButton.addMouseListener(this);
addUserButton.addDLMListener(this);
corkArea.add(addUserButton);

addAdminButton = new O2Button("Add Admin");
addAdminButton.setVisible(false);
addAdminButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
addAdminButton.setLocation(addUserButton.getX(), addUserButton.getY() - addAdminButton.getHeight());
addAdminButton.addMouseListener(this);
addAdminButton.addDLMListener(this);
corkArea.add(addAdminButton);

deleteUserButton = new O2Button("Delete User");
deleteUserButton.setVisible(false);
deleteUserButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
deleteUserButton.setLocation(addUserButton.getX(), addAdminButton.getY() - deleteUserButton.getHeight());
deleteUserButton.addMouseListener(this);
deleteUserButton.addDLMListener(this);
corkArea.add(deleteUserButton);

exitButton = new O2Button("Exit");
exitButton.setSize(BUTTON_WIDTH / 2, BUTTON_HEIGHT);
exitButton.setLocation(this.getWidth() - exitButton.getWidth(), 0);
exitButton.addMouseListener(this);
exitButton.addDLMListener(this);
corkArea.add(exitButton);

atTrayButton = new O2Button("TrayBar");
atTrayButton.setSize(BUTTON_WIDTH / 2, BUTTON_HEIGHT);
atTrayButton.setLocation(exitButton.getX() - atTrayButton.getWidth(), 0);
atTrayButton.addMouseListener(this);
atTrayButton.addDLMListener(this);
corkArea.add(atTrayButton);

widgets = new Vector<StickyNoteWidget>();

// carichiamo tutte le note dal database
Vector<GenericStickyNote> tmp = StickyNoteDb.getIstance().getNotes();

// aggiungiamo alla bacheca tutti i widget relativi alle note nel databsae
for(int i = 0; i < tmp.size(); i++)
widgets.add(new StickyNoteWidget(this, corkArea, tmp.get(i)));
}

private void handleEvent(Object obj)
{
if(obj == loginButton)
{
// se non esiste un utente loggato
// mostriamo la schermata di login
if(!LoginEnvironment.isLogged())
new LoginWindow(this, this);

// altrimenti significa che l'utente vuole scollegarsi
else
logout();
}
else if(obj == addNoteButton)
// facciamo inserire una nuova nota generica all'utente
widgets.add(new StickyNoteWidget(this, corkArea, StickyNoteWidget.GENERIC_NOTE));
else if(obj == addAnswerButton)
// facciamo inserire una nuova domanda sì/no all'utente
widgets.add(new StickyNoteWidget(this, corkArea, StickyNoteWidget.ANSWER_NOTE));
else if(obj == addExtendedAnswerButton)
// facciamo inserire una nuova domanda con testo all'utente
widgets.add(new StickyNoteWidget(this, corkArea, StickyNoteWidget.EXTENDED_ANSWER_NOTE));
else if(obj == addAlarmNoteButton)
// facciamo inserire una nuova sveglia all'utente
widgets.add(new StickyNoteWidget(this, corkArea, StickyNoteWidget.ALARM_NOTE));
else if(obj == addUserButton)
// facciamo inserire un nuovo utente
new AddUserWindow(this, User.USER);
else if(obj == addAdminButton)
// facciamo inserire un nuovo amministratore
new AddUserWindow(this, User.ADMIN);
else if(obj == deleteUserButton)
// facciamo cancellare un utente/amministratore
new DeleteUserWindow(this, corkArea);
else if(obj == atTrayButton)
{
// riduciamo il programma nella trayBar
iconify();
TrayStickyNotes.setText(TrayStickyNotes.DEFAULT_TOOLTIP);
}
else if(obj == exitButton)
// avviamo la procedura di chiusura
close();
}

public void iconify()
{
// otteniamo il riferimento all'ambiente grafico
// e se è disponibile il fullscreen
// dobbiamo rimuovere questa finestra dal fullscreen
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
if (gs.isFullScreenSupported())
{
gs.setFullScreenWindow(null);
setVisible(false);
}
else
setVisible(false);
}

public void showApp()
{
// otteniamo il riferimento all'ambiente grafico
// e se è disponibile il fullscreen settiamo questa finestra
// a tutto schermo
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
if (gs.isFullScreenSupported())
{
// Full-screen mode is supported
gs.setFullScreenWindow(this);
}
// altrimenti rendiamo solo visibile la finestra
else
System.out.println("Fullscreen Non supportato");

setVisible(true);
}

public void close()
{
// liberiamo tutti gli ascoltatori
loginButton.removeMouseListener(this);
addNoteButton.removeMouseListener(this);
addAnswerButton.removeMouseListener(this);
addExtendedAnswerButton.removeMouseListener(this);
addAlarmNoteButton.removeMouseListener(this);
addUserButton.removeMouseListener(this);
addAdminButton.removeMouseListener(this);
deleteUserButton.removeMouseListener(this);
atTrayButton.removeMouseListener(this);
exitButton.removeMouseListener(this);

loginButton.removeDLMListener();
addNoteButton.removeDLMListener();
addAnswerButton.removeDLMListener();
addExtendedAnswerButton.removeDLMListener();
addAlarmNoteButton.removeDLMListener();
addUserButton.removeDLMListener();
addAdminButton.removeDLMListener();
deleteUserButton.removeDLMListener();
atTrayButton.removeDLMListener();
exitButton.removeDLMListener();

// salviamo la configurazione
StickyNoteDb.getIstance().saveConfig();
// quindi terminiamo il programma
System.exit(0);
}

public void login(boolean esito, User usr)
{
if(esito)
{
// se l'esito è positivo significa che l'utente è nel database
// quindi lo logghiamo al sistema e rendiamo disponibili
// le operazioni in base ai suoi privilegi
this.loginButton.setText("Logout");
addUserButton.setVisible(usr.isAdmin());
addAdminButton.setVisible(usr.isAdmin());
deleteUserButton.setVisible(usr.isAdmin());
LoginEnvironment.login(usr);
}
else
// altrimenti notifichiamo il fallimento all'utente
this.loginButton.setText("Dati incorretti. Riprova");

// in base all'esito del login visualizziamo o meno
// le operazioni di gestione delle note
addNoteButton.setVisible(esito);
addAnswerButton.setVisible(esito);
addExtendedAnswerButton.setVisible(esito);
addAlarmNoteButton.setVisible(esito);
}

public void logout()
{
// rimuoviamo la possibilità di utilizzare funzioni
// quindi disconnettiamo l'utente dal sistema
loginButton.setText("Login");
addNoteButton.setVisible(false);
addAnswerButton.setVisible(false);
addExtendedAnswerButton.setVisible(false);
addAlarmNoteButton.setVisible(false);
addUserButton.setVisible(false);
addAdminButton.setVisible(false);
deleteUserButton.setVisible(false);
LoginEnvironment.logout();
}

public void mouseClicked(MouseEvent e)
{
handleEvent(e.getSource());
}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

public void pressEvent(PressEvent e) {}

public void moveEvent(MoveEvent e) {}

public void releaseEvent(ReleaseEvent e) {}

public void clickEvent(ClickEvent e)
{
handleEvent(e.getSource());
}

public void zoomEvent(ZoomEvent e) {}

public void runApp()
{
showApp();
}
}


LoginWindow non è altro che un wrapper per VKeyboard

package houseboard.Vkeyboard;

import DLMListener.DLMListener;
import MoveNClick.*;
import OxygenGraphics.*;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JLabel;
import javax.swing.JTextArea;

/**
*
* @author gioele
*/
public class VKeyboard extends O2Window implements DLMListener, MouseListener
{
public final static boolean PASSWORD_FIELD = true;
private static final Color backgroundColor = new Color(187, 232, 255);
private final static int PER_LINE_CHAR = 12;
private final static String[] CHAR_MAP = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "'", "ì",
"q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "è", "é",
"a", "s", "d", "f", "g", "h", "j", "k", "l", "ò", "à", "ù",
"<", "z", "x", "c", "v", "b", "n", "m", ",", ".", "?", "!"};
private static int charSize;
private static int topLine;
private static int leftLine;

private JTextArea textarea;
private O2Button backspace, ok, whitespace, shift, clear, esc, newLine;
private O2Button[] chars;
private VKeyboardListener caller;
private String text;
private boolean secureMode;
private boolean capsLock;
private Window owner;

public VKeyboard(Window owner, String s,VKeyboardListener caller)
{
super(owner);
this.owner = owner;

secureMode = false;
// settiamo l'ascoltatore della VKeyboard
this.caller = caller;

initialize(s);
}

public VKeyboard(Window owner, String s, VKeyboardListener caller, boolean secure)
{
super(owner);
this.owner = owner;

secureMode = secure;
// settiamo l'ascoltatore della VKeyboard
this.caller = caller;

initialize(s);
}

private void initialize(String s)
{
int screenWidth = Toolkit.getDefaultToolkit().getScreenSize().width;
int screenHeight = Toolkit.getDefaultToolkit().getScreenSize().height;
// fullscreen
setSize(screenWidth, screenHeight);
addDLMListener(this);
addMouseListener(this);

charSize = screenWidth / 14;

chars = new O2Button[CHAR_MAP.length];
// centriamo la tastiera rispetto alla risoluzione dello schermo
topLine = screenHeight / 2 // centro dello schermo
// mezza tastiera (compresa la textarea) deve essere sopra
- (charSize + charSize * CHAR_MAP.length / PER_LINE_CHAR - charSize * 4 ) / 2;
leftLine = screenWidth / 2 // centro dello schermo
// mezza tastiera deve essere a sinistra (il +2 sta ad indicare la larghezza del tasto OK, CLEAR, ecc.
- (charSize * (PER_LINE_CHAR + 2)) / 2;

// di default non scriviamo in maiuscolo
capsLock = false;

Container pane = this.getContentPane();

// null layout
pane.setLayout(null);
pane.setBackground(backgroundColor);

// header
JLabel header = new JLabel();
header.setFont(new Font("Serif", Font.BOLD, 15));
header.setForeground(Color.RED);
header.setHorizontalAlignment( JLabel.CENTER );
header.setText(s);

O2Panel headerPanel = new O2Panel();
headerPanel.setSize(charSize * (PER_LINE_CHAR + 2), charSize);
headerPanel.setLocation(leftLine, 0);
headerPanel.setBackground(backgroundColor);
headerPanel.add(header);
pane.add(headerPanel);

// textarea
textarea = new JTextArea();
textarea.setSize(charSize * (PER_LINE_CHAR + 2), charSize * 2);
textarea.setLocation(leftLine, headerPanel.getY() + headerPanel.getHeight());
textarea.setBackground(backgroundColor);
textarea.setFont(new Font("Serif", Font.PLAIN, charSize / 2));
pane.add(textarea);

// newLine button
newLine = new O2Button("NEW LINE");
newLine.setSize(charSize * 2, charSize);
newLine.setLocation(leftLine + charSize * PER_LINE_CHAR,
topLine + charSize * 3);
newLine.addMouseListener(this);
newLine.addDLMListener(this);
pane.add(newLine);

// clear button
clear = new O2Button("CLR");
clear.setSize(charSize, charSize);
clear.setLocation(leftLine + charSize * PER_LINE_CHAR,
topLine + charSize * 4);
clear.addMouseListener(this);
clear.addDLMListener(this);
pane.add(clear);

// esc button
esc = new O2Button("ESC");
esc.setSize(charSize, charSize);
esc.setLocation(clear.getX() + clear.getWidth(), clear.getY());
esc.addMouseListener(this);
esc.addDLMListener(this);
pane.add(esc);

// shift button
shift = new O2Button("SHIFT");
shift.setSize(charSize * 2, charSize);
shift.setLocation(leftLine,
topLine + charSize * (chars.length / PER_LINE_CHAR) );
shift.addMouseListener(this);
shift.addDLMListener(this);
pane.add(shift);

// backspace button
backspace = new O2Button("<-");
backspace.setSize(charSize * 2, charSize);
backspace.setLocation(leftLine + charSize * PER_LINE_CHAR,
topLine);
backspace.addMouseListener(this);
backspace.addDLMListener(this);
pane.add(backspace);

// ok button
ok = new O2Button("OK");
ok.setSize(charSize * 2, charSize * 2);
ok.setLocation(leftLine + charSize * PER_LINE_CHAR,
topLine + charSize);
ok.addMouseListener(this);
ok.addDLMListener(this);
pane.add(ok);

// space button
whitespace = new O2Button("Spazio");
whitespace.setSize(charSize * (PER_LINE_CHAR - 2) , charSize);
whitespace.setLocation(leftLine + charSize * 2,
topLine + charSize * (chars.length / PER_LINE_CHAR));
whitespace.addMouseListener(this);
whitespace.addDLMListener(this);
pane.add(whitespace);

// tutti i caratteri
for(int i = 0; i < chars.length; i++)
{
chars[i] = new O2Button(CHAR_MAP[i]);
chars[i].setSize(charSize,charSize);
// nuova riga ogni PER_LINE_CHAR caratteri
chars[i].setLocation(leftLine + charSize * (i % PER_LINE_CHAR),
topLine + charSize * (i / PER_LINE_CHAR));

chars[i].addMouseListener(this);
chars[i].addDLMListener(this);
pane.add(chars[i]);
}

text = "";

// visualizziamo quindi la Virtual Keyboard
setVisible(true);
}

private void concludi()
{
// ritorniamo il testo scritto
if(caller != null)
caller.VKeyboardTextReceived(text);
// rimuoviamo tutti gli ascoltatori
removeDLMListener();
backspace.removeMouseListener(this);
backspace.removeDLMListener();
ok.removeMouseListener(this);
ok.removeDLMListener();
whitespace.removeMouseListener(this);
whitespace.removeDLMListener();
shift.removeMouseListener(this);
shift.removeDLMListener();
clear.removeMouseListener(this);
clear.removeDLMListener();
esc.removeMouseListener(this);
esc.removeDLMListener();
newLine.removeMouseListener(this);
newLine.removeDLMListener();

for(int i = 0; i < chars.length; i++)
{
chars[i].removeMouseListener(this);
chars[i].removeDLMListener();
}

// possiamo cancellare a questo punto la Virtual Keyboard
dispose();
}

private void handleEvent(Object obj)
{
if(obj == ok)
concludi();
else if(obj == whitespace)
text += " ";
else if(obj == clear)
text = "";
else if(obj == esc)
{
text = null;
concludi();
}
else if(obj == shift)
{
capsLock = !capsLock;
if(capsLock)
shift.setBackground(Color.GREEN);
else
shift.setBackground(null);
}
else if(obj == backspace)
{
if(text.length() > 0)
text = text.substring(0, text.length() - 1);
}
else if(obj == newLine)
text += "\n";
else
{
for(int i = 0; i < chars.length; i++)
if(obj == chars[i])
if(capsLock)
text += CHAR_MAP[i].toUpperCase();
else
text += CHAR_MAP[i];
}

if(!secureMode)
textarea.setText(text);
else
{
textarea.setText("");
if(text != null)
for(int i = 0; i < text.length(); i++)
textarea.append("*");
}
}

public void pressEvent(PressEvent e){}

public void moveEvent(MoveEvent e){}

public void releaseEvent(ReleaseEvent e) {}

public void clickEvent(ClickEvent e)
{
handleEvent(e.getSource());
}

public void zoomEvent(ZoomEvent e) {}

public void mouseClicked(MouseEvent e)
{
handleEvent(e.getSource());
}

public void mousePressed(MouseEvent e) {}

public void mouseReleased(MouseEvent e) {}

public void mouseEntered(MouseEvent e) {}

public void mouseExited(MouseEvent e) {}

}


Ebbene, quando clicco sul pulsante in alto LOGIN si apre la window della Vkeyboard ma rimane visualizzato anche Login (non sempre). Sinceramente ho anche provato a fare il repaint manualmente e in effetti il problema è quello perchè quando premevo un tasto sulla VirtualKeyboard il problema scompariva (avevo settato un repaint() nell'ascoltatore dei tasti :P), ma non può essere questa la soluzione.

Giusto?

Gio Games
24-06-2010, 21:18
Fermi tutti :D .

Ho trovato la parte incriminata:


GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();

if (gs.isFullScreenSupported())
{
// Full-screen mode is supported
gs.setFullScreenWindow(this);
}


Facendo delle prove ho notato che il problema di rendering lo ritrovo solo se setto il Fullscreen, mentre lasciando la JWindow a tutto schermo ma non impostando il fullscreen non ho problemi.

Sinceramente non capisco bene il motivo, non credo ad un problema locale, dato che ho provato l'applicativo su due macchine diverse con Xp, perchè faccio notare che su Debian non ho avuto questi problemi: credo che sia dovuto molto al layer sottostante, infatti facendo girare una virtual machine con Xp (guest) su Debian (host) non ho avuto di questi problemi. Effettivamente è abbastanza strano ma credo che sia dovuto tutto alle primitive chiamate dalla JVM.

Spero di non aver detto castronerie e che questo possa servire a qualcuno in futuro

banryu79
25-06-2010, 12:28
Ciao, oggi sono un po' di corsa al lavoro e non ho ancora avuto modo di leggere il codice che hai postato.
Però posso subito risponderti a questo:

Facendo delle prove ho notato che il problema di rendering lo ritrovo solo se setto il Fullscreen, mentre lasciando la JWindow a tutto schermo ma non impostando il fullscreen non ho problemi.

Sinceramente non capisco bene il motivo...

Potrebbe essere dovuto al fatto che stai usando una finestra in modalità esclusiva a tutto schermo con rendering di tipo passivo (cioè quello supportato normalmente dal AWT/Swing, basato sulle chiamate al metodo paint).

Per maggiori dettagli sulla differenza tra "active rendering" e "passive rendering" guarda qua (http://www.gamedev.net/reference/articles/article2418.asp).

Non so se hai già letto questa pagina (http://java.sun.com/docs/books/tutorial/extra/fullscreen/rendering.html), parla del Passive vs Active rendering in Swing quando si usa la Fullscreen Exclusive Mode API (come sembra tu stia facendo).

Gio Games
26-06-2010, 09:55
Ecco allora è sicuramente come hai detto tu, io infatti sto utilizzando rendering passivo. Ti ringrazio per l'informazione ;)

A questo punto credo che allora rinuncerò al fullscreen esclusivo, dovrei stravolgere in parte l'applicazione e il vantaggio che avrei non sarebbe poi così marcato.

Grazie di tutto veramente, è stato un piacere scambiare quattro chiacchiere con te ;)