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