1   package kerhoswing;
2   
3   import java.awt.Color;
4   import java.awt.Desktop;
5   import java.awt.event.ActionEvent;
6   import java.awt.event.ActionListener;
7   import java.awt.event.KeyAdapter;
8   import java.awt.event.KeyEvent;
9   import java.io.IOException;
10  import java.io.PrintStream;
11  import java.net.URI;
12  import java.net.URISyntaxException;
13  import java.util.Collection;
14  import java.util.List;
15  import javax.swing.JComboBox;
16  import javax.swing.JComponent;
17  import javax.swing.JLabel;
18  import javax.swing.JOptionPane;
19  import javax.swing.JTextArea;
20  import javax.swing.JTextField;
21  import kerho.Harrastus;
22  import kerho.Jasen;
23  import kerho.Kerho;
24  import kerho.SailoException;
25  import fi.jyu.mit.gui.EditPanel;
26  import fi.jyu.mit.gui.AbstractChooser;
27  import fi.jyu.mit.gui.IStringListChooser;
28  import fi.jyu.mit.gui.IStringTable;
29  import fi.jyu.mit.gui.SelectionChangeListener;
30  import fi.jyu.mit.gui.StringTable;
31  import fi.jyu.mit.gui.TextAreaOutputStream;
32  
33  /**
34   * Luokka joka käsittelee kerhoa Swing-komponenteilla.
35   * Luokan käyttö:
36   * <pre>
37   * 1) Tee jollakin tavalla lomake, jossa on tarvittavat komponentit
38   * 2) Laita lomakkeelle myös Lisää ja Talleta -napit niin halutessasi
39   * 3) Luo koodissa lomakkeella KerhoSwing-olio
40   * <pre>
41   *        kerhoswing = new KerhoSwing();
42   *        kerhoswing.setListJasenet(listJasenet);
43   *        kerhoswing.setEditHaku(editHaku);
44   *        kerhoswing.setCbKentat(cbKentat);
45   *        kerhoswing.setHarrastuksetTable(tableHarrastus);
46   *        kerhoswing.setPanelJasen(boxJasen);
47   *        kerhoswing.setLabelVirhe(labelVirhe);
48   *        
49   *        String virhe = kerhoswing.lueTiedosto("kelmit");
50   *        if ( virhe != null ) JOptionPane.showMessageDialog(null, virhe);
51   * </pre>       
52   * 4) Kutsu nappien tapahtumista mm. olion lisaa ja talleta -metodeja.
53   *         kerhoswing.lisaa();
54   *         kerhoswing.talleta();
55   * 5) Jotta kaikki tulee talletettua, kutsu ohjelmasta poistuvissa paikoissa
56   *         kerhoswing.talleta();        
57   * 6) Harrastukset-taulukosta tulee tapahtuma kun jonkin solun sisältö muuttuu.
58   *    Kutsu tällöin:
59   *    <pre>         
60   *      tableHarrastus.addTableEditListener(new TableEditListener() {
61   *          @Override
62   *          public String tableEdit(IStringTable sender, int row, int column, Object s) {
63   *              return kerhoswing.setHarrastus(sender,row,column,s);
64   *          }
65   *      });
66   * </pre>
67   * @author vesal
68   * @version 10.4.2009
69   * @version 1.1.2011 - lisätty harrastusten käsittely
70   * @version 1.5.2011 - harrastukset muutettu StringTablen avulla toimivaksi
71   */
72  public class KerhoSwing {
73  
74      private static Color virheVari = new Color(255, 0, 0);
75      private static Color normaaliVari = new Color(255, 255, 255);
76      private final static Jasen apujasen = new Jasen();
77      private JComboBox cbKentat;
78      private JTextField editHaku;
79      private AbstractChooser<Jasen> listJasenet;
80      private StringTable tableHarrastukset;
81      private JComponent panelJasen;
82      private JLabel labelVirhe;
83  
84      private final Kerho kerho;
85      private Jasen jasenKohdalla; // Kohdalla oleva jäsen
86      private Jasen editJasen; // Kohdalla oleva jäsen jos tehty muutoksia
87      private EditPanel editsj[]; // Jäsenen kenttiä vastaavat edit-kentät
88  
89  
90      /**
91       * Alustaa luokan niin, että se voi käyttää  Swing-komponentteja
92       */
93      public KerhoSwing() {
94          kerho = new Kerho();
95      }
96  
97  
98      /**
99       * Alustaa luokan niin, että tuodaan valmiiksi luotu kerho parametrina
100      * @param kerho valmiiksi luotu kerho, jota käytetään
101      */
102     public KerhoSwing(Kerho kerho) {
103         this.kerho = kerho;
104     }
105 
106 
107     /**
108      * @return mihin näytetään virheteksti
109      */
110     public JLabel getLabelVirhe() {
111         return labelVirhe;
112     }
113 
114 
115     /**
116      * @param labelVirhe mihin näytetään virheteksti
117      */
118     public void setLabelVirhe(JLabel labelVirhe) {
119         this.labelVirhe = labelVirhe;
120     }
121 
122 
123     /**
124      * @return combobox jossa kenttälista 
125      */
126     public JComboBox getCbKentat() {
127         return cbKentat;
128     }
129 
130 
131     /**
132      * @param cbKentat comboboxkenttälistaa varten
133      */
134     public void setCbKentat(JComboBox cbKentat) {
135         this.cbKentat = cbKentat;
136     }
137 
138 
139     /**
140      * @return edit jossa hakuehto
141      */
142     public JTextField getEditHaku() {
143         return editHaku;
144     }
145 
146 
147     /**
148      * @param editHaku edit johon saa kirjoittaa hakuehdon
149      */
150     public void setEditHaku(JTextField editHaku) {
151         this.editHaku = editHaku;
152     }
153 
154 
155     /**
156      * @return lista johon jäsenet laitetaan
157      */
158     public AbstractChooser<Jasen> getListJasenet() {
159         return listJasenet;
160     }
161 
162 
163     /**
164      * @param listJasenet lista johon jäsenet laitetaan
165      */
166     @SuppressWarnings({ "rawtypes", "unchecked" })
167     public void setListJasenet(AbstractChooser listJasenet) {
168         this.listJasenet = listJasenet;
169     }
170 
171 
172     /**
173      * @return taulukko johon tulee jäsenen harrastukset
174      */
175     public StringTable getStringTable() {
176         return tableHarrastukset;
177     }
178 
179 
180     /**
181      * @param tableHarrastukset taulukko johon tulee jäsenen harrastukset
182      */
183     public void setTableHarrastukset(StringTable tableHarrastukset) {
184         this.tableHarrastukset = tableHarrastukset;
185     }
186 
187 
188     /**
189      * @return alue johon lisätään jäsenen yksittäiset kentät.
190      */
191     public JComponent getPanelJasen() {
192         return panelJasen;
193     }
194 
195 
196     /**
197      * @param panelJasen alue johon lisätään jäsenen tiedot.  Mielellään joku johon tulevat alekkain
198      */
199     public void setPanelJasen(JComponent panelJasen) {
200         this.panelJasen = panelJasen;
201     }
202 
203 
204     /**
205      * Tämä alustaa valitut alueet käyttökuntoon.
206      */
207     public void alusta() {
208         if ( editsj != null ) return; // on jo alustettu
209         cbKentat.removeAllItems();
210 
211         for (int k = apujasen.ekaKentta(); k < apujasen.getKenttia(); k++) {
212             cbKentat.addItem(apujasen.getKysymys(k));
213         }
214 
215         editHaku.addKeyListener(new KeyAdapter() {
216             @Override
217             public void keyReleased(KeyEvent e) {
218                 hae(0);
219             }
220         });
221 
222         cbKentat.addActionListener(new ActionListener() {
223             @Override
224             public void actionPerformed(ActionEvent e) {
225                 hae(0);
226             }
227         });
228 
229         listJasenet.addSelectionChangeListener(new SelectionChangeListener<Jasen>() {
230             @Override
231             public void selectionChange(IStringListChooser<Jasen> sender) {
232                 naytaJasen();
233             }
234         });
235 
236         luoNaytto();
237         hae(0);
238     }
239 
240 
241     /**
242      * Asetetaan editoitava jäsen
243      * @param j uusi viite editJasenelle
244      */
245     private void setEditJasen(Jasen j) {
246         editJasen = j;
247     }
248 
249 
250     /**
251      * Tekee uuden tyhjän jäsenen editointia varten
252      */
253     public void uusiJasen() {
254         tarkistaMuutos();
255         jasenKohdalla = new Jasen();
256         jasenKohdalla.rekisteroi();
257         setEditJasen(null);
258         laitaJasen();
259         listJasenet.clearSelection();
260     }
261 
262 
263     /**
264      * Tekee uuden tyhjän harrastuksen editointia varten
265      */
266     public void uusiHarrastus() {
267         if ( jasenKohdalla == null ) return;
268         tarkistaMuutos();
269         Harrastus harrastus = new Harrastus(jasenKohdalla.getTunnusnro());
270         harrastus.rekisteroi();
271         kerho.lisaa(harrastus);
272         naytaHarrastukset();
273         tableHarrastukset.selectCell(tableHarrastukset.getRowCount()-1, 0);
274     }
275 
276 
277     /**
278      * Näytetään harrastukset taulukkoon.  Tyhjennetään ensin taulukko ja sitten
279      * lisätään siihen kaikki harrastukset
280      */
281     protected void naytaHarrastukset() {
282         if ( jasenKohdalla == null ) return;
283         tableHarrastukset.clear();
284         
285         List<Harrastus> harrastukset = kerho.annaHarrastukset(jasenKohdalla);
286         for (Harrastus har : harrastukset) {
287             int r = tableHarrastukset.addRow();
288             tableHarrastukset.setObjectAt(har, r);
289             for (int k = har.ekaKentta(); k < har.getKenttia(); k++) {
290                 String jono = har.anna(k);
291                 tableHarrastukset.setValueAtModel(jono, r, k - har.ekaKentta());
292             }
293         }
294 
295     }
296 
297 
298     /**
299      * Lukee kerhon tiedot tiedostosta. Tarkistetaan aluksi että kaikki
300      * kentät on asetettu paikalleen.
301      * @param s tiedoston nimi
302      * @return null jos onnistuu, muuten virheilmoitus
303      */
304     public String lueTiedosto(String s) {
305         try {
306             if ( cbKentat == null ) return "cbKentat on alustamatta";
307             if ( editHaku == null ) return "editHaku on alustamatta";
308             if ( listJasenet == null ) return "listJasenet on alustamatta";
309             if ( tableHarrastukset == null ) return "tableHarrastukset on alustamatta";
310             if ( panelJasen == null ) return "panelJasen on alsutamatta";
311             alusta();
312             kerho.lueTiedostosta(s);
313             hae(0);
314             return null;
315         } catch (SailoException e) {
316             hae(0);
317             uusiJasen();
318             return e.getMessage();
319         }
320 
321     }
322 
323 
324     /**
325      * Tutkii onko editoitavaan jäseneen tehty muutoksia, jotka kannattaa
326      * tallettaa.
327      * @return false jos ei muutoksia
328      */
329     private boolean muuttunut() {
330         if ( jasenKohdalla == null ) return false;
331         if ( editJasen == null ) return false;
332         return !jasenKohdalla.equals(editJasen);
333     }
334 
335 
336     /**
337      * Tallettaa nykyisen mahdollisesti muutetun jäsenen ja sitten koko tiedoston
338      * @return null jos menee hyvin, muuten virheteksti
339      */
340     public String talleta() {
341         try {
342             if ( muuttunut() ) {
343                 kerho.korvaaTaiLisaa(editJasen);
344                 jasenKohdalla = editJasen;
345             }
346             setEditJasen(null);
347             if ( jasenKohdalla != null ) {
348                 int jnro = jasenKohdalla.getTunnusnro();
349                 hae(jnro);
350             }
351             kerho.talleta();
352             return null;
353         } catch (SailoException ex) {
354             JOptionPane.showMessageDialog(null, "Talletuksessa ongelmia! " + ex.getMessage());
355             return ex.getMessage();
356         }
357     }
358 
359 
360     /**
361      * Suorittaa niiden jäsenten hakemisen, joiden valittu kenttä täyttää hakuehdon
362      * @param jnro jäsenen numero, joka aktivoidaan haun jälkeen
363      */
364     protected void hae(int jnro) {
365         int k = cbKentat.getSelectedIndex() + apujasen.ekaKentta();
366         String ehto = editHaku.getText();
367         if ( ehto.indexOf('*') < 0 ) {
368             ehto = "*" + ehto + "*";
369         }
370         Collection<Jasen> tulos = kerho.etsi(ehto, k);
371         listJasenet.clear();
372 
373         int i = 0, index = 0;
374         for (Jasen j : tulos) {
375             if ( j.getTunnusnro() == jnro ) index = i;
376             listJasenet.add(j.getNimi(), j);
377             i++;
378         }
379         listJasenet.setSelectedIndex(index);
380     }
381 
382 
383     /**
384      * Laittaa jäsenen tiedot edit-kenttiin
385      */
386     private void laitaJasen() {
387         if ( jasenKohdalla == null ) return;
388         for (int i = 0, k = jasenKohdalla.ekaKentta(); k < jasenKohdalla.getKenttia(); k++, i++) {
389             editsj[i].setText(jasenKohdalla.anna(k));
390             editsj[i].getEdit().setBackground(normaaliVari);
391             editsj[i].setToolTipText("");
392         }
393         naytaHarrastukset();
394     }
395 
396 
397     /**
398      * Tarkitetaan onko jäsenen tiedot muuttuneet ja jos on, kysytään halutaanko tallentaa
399      */
400     public void tarkistaMuutos() {
401         if ( muuttunut() ) {
402             int vastaus = JOptionPane.showConfirmDialog(null, "Talletetaanko?", "Jäsen muuttunut!", JOptionPane.YES_NO_OPTION);
403             if ( vastaus == JOptionPane.YES_OPTION ) talleta();
404         }
405     }
406 
407 
408     /**
409      * Näyttää listasta valitun jäsenen tiedot
410      */
411     protected void naytaJasen() {
412         int ind = listJasenet.getSelectedIndex();
413         if ( ind < 0 ) return;
414         tarkistaMuutos();
415        // listJasenet.setSelectedIndex(ind);
416         jasenKohdalla = listJasenet.getSelectedObject();
417         setEditJasen(null);
418         laitaJasen();
419     }
420 
421 
422     /**
423      * Käsittelee edit-kenttään tulleen muutoksen jäseneen.
424      * Mikäli jäsentä ei vielä ole editoitu, luodaan jäsenestä
425      * kopio editJasen-olioon ja muutokset kohdistetaan tähän
426      * jäseneen.  Näin voidaan muutoksia verrata jasen-olioon
427      * ja tunnistetaan muutostarpeet.  Jos muutosta ei voida
428      * sijoittaa jäseneen, muutetaan taustaväri punaiseksi.
429      * @param edit muuttunut kenttä
430      */
431     protected void kasitteleMuutosJaseneen(JTextField edit) {
432         if ( jasenKohdalla == null ) {
433             jasenKohdalla = new Jasen();
434             jasenKohdalla.rekisteroi();
435             setEditJasen(null);
436         }
437         if ( editJasen == null )
438             try {
439                 setEditJasen(jasenKohdalla.clone());
440             } catch (CloneNotSupportedException e) { // virhettä ei tule
441             }
442         String s = edit.getText();
443         int k = Integer.parseInt(edit.getName().substring(2));
444         String virhe = editJasen.aseta(k, s);
445         setVirhe(virhe);
446         if ( virhe == null ) {
447             edit.setToolTipText("");
448             edit.setBackground(normaaliVari);
449         } else {
450             edit.setToolTipText(virhe);
451             edit.setBackground(virheVari);
452         }
453     }
454 
455 
456     /**
457      * Laitetaan virheilmoitus näkyville jos labelVirhe on alustettu.
458      * Mikäli virhettä ei ole, niin piilotetaan virheilmoitus.    
459      * @param virhe virheteksti
460      */
461     public void setVirhe(String virhe) {
462         if ( labelVirhe == null ) return;
463         if ( virhe == null ) {
464             labelVirhe.setVisible(false);
465             return;
466         }
467         labelVirhe.setVisible(true);
468         labelVirhe.setText(virhe);
469         labelVirhe.setBackground(virheVari);
470     }
471 
472 
473     /**
474      * Luo panelJasen:een jäsenen kenttiä vastaavat labelit ja edit-kentät
475      */
476     private void luoNaytto() {
477         int n = apujasen.getKenttia() - apujasen.ekaKentta();
478         editsj = new EditPanel[n];
479         panelJasen.removeAll();
480 
481         for (int i = 0, k = apujasen.ekaKentta(); k < apujasen.getKenttia(); k++, i++) {
482             EditPanel edit = new EditPanel(); // NOPMD
483             edit.setCaption(apujasen.getKysymys(k));
484             editsj[i] = edit;
485             edit.setName("ej" + k);
486             edit.getEdit().setName("tj" + k);
487             panelJasen.add(edit);
488             edit.addKeyListener(new KeyAdapter() { // NOPMD
489                 @Override
490                 public void keyReleased(KeyEvent e) {
491                     kasitteleMuutosJaseneen((JTextField)e.getComponent());
492                 }
493             });
494 
495         }
496     }
497 
498 
499     /**
500      * Poistetaan harrastustaulukosta valitulla kohdalla oleva harrastus.
501      */
502     public void poistaHarrastus() {
503         int rivi = tableHarrastukset.getSelectedRow();
504         if ( rivi < 0 ) return;
505         Harrastus harrastus = (Harrastus)tableHarrastukset.getSelectedObject();
506         if ( harrastus == null ) return;
507         kerho.poistaHarrastus(harrastus);
508         naytaHarrastukset();
509         if ( rivi >= tableHarrastukset.getRowCount() ) rivi = tableHarrastukset.getRowCount() - 1;
510         tableHarrastukset.selectCell(rivi, 0);
511     }
512 
513 
514     /**
515      * Poistetaan listasta valittu jäsen.
516      */
517     public void poistaJasen() {
518         if ( jasenKohdalla == null ) return;
519         int vastaus = JOptionPane.showConfirmDialog(null, "Poistetaanko jäsen: " + jasenKohdalla.getNimi(), "Poisto?", JOptionPane.YES_NO_OPTION);
520         if ( vastaus == JOptionPane.NO_OPTION ) return;
521         kerho.poista(jasenKohdalla.getTunnusnro());
522         int index = listJasenet.getSelectedIndex();
523         hae(0);
524         listJasenet.setSelectedIndex(index);
525     }
526 
527 
528     /**
529      * Avataan ulkoinen selain näyttämään avustustekstiä.
530      */
531     public void avustus() {
532         Desktop desktop = Desktop.getDesktop();
533         try {
534             //URI uri = new URI("kerho.html");
535             URI uri = new URI("https://trac.cc.jyu.fi/projects/ohj2k11/wiki/vesal2");
536             desktop.browse(uri);
537         } catch (URISyntaxException e) {
538             return;
539         } catch (IOException e) {
540             return;
541         }
542     }
543 
544 
545     /**
546      * Tulostaa jäsenen tiedot
547      * @param os tietovirta johon tulostetaan
548      * @param jasen tulostettava jäsen
549      */
550     public void tulosta(PrintStream os, final Jasen jasen) {
551         os.println("----------------------------------------------");
552         jasen.tulosta(os);
553         os.println("----------------------------------------------");
554         for (Harrastus har : kerho.annaHarrastukset(jasen))
555             har.tulosta(os);
556     }
557 
558 
559     /**
560      * Tulostaa listassa olevat jäsenet tekstialueeseen
561      * @param text alue johon tulostetaan
562      */
563     public void tulostaValitut(JTextArea text) {
564         PrintStream os = TextAreaOutputStream.getTextPrintStream(text);
565         for (Jasen jasen : listJasenet.getObjectList()) {
566             tulosta(os, jasen);
567             os.println("\n\n");
568         }
569     }
570 
571 
572     /**
573      * Asetetaan harrastukseen uusi arvo
574      * @param sender mistä taulukosta pyyntö tuli
575      * @param row miltä riviltä
576      * @param column mistä sarakkeesta
577      * @param s mitä haluttiin laittaa
578      * @return null jos ok, muuten virhe
579      */
580     public String setHarrastus(IStringTable sender, int row, int column, Object s) {
581         Harrastus har = (Harrastus)sender.getObjectAt(row);
582         if ( har == null ) return "Ei harrastusta";
583         String virhe = har.aseta(column+har.ekaKentta(), s.toString());
584         if ( virhe == null ) kerho.setHarrastusMuutos();
585         return virhe;
586     }
587 }
588