001: /*
002: * Created on 10/01/2004
003: *
004: * ============================================================================
005: * GNU Lesser General Public License
006: * ============================================================================
007: *
008: * Swing Components - visit http://sf.net/projects/gfd
009: *
010: * Copyright (C) 2004 Igor Regis da Silva Simões
011: *
012: * This library is free software; you can redistribute it and/or
013: * modify it under the terms of the GNU Lesser General Public
014: * License as published by the Free Software Foundation; either
015: * version 2.1 of the License, or (at your option) any later version.
016: *
017: * This library is distributed in the hope that it will be useful,
018: * but WITHOUT ANY WARRANTY; without even the implied warranty of
019: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
020: * Lesser General Public License for more details.
021: *
022: * You should have received a copy of the GNU Lesser General Public
023: * License along with this library; if not, write to the Free Software
024: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
025: */
026:
027: package br.com.gfpshare.beans;
028:
029: import java.awt.AWTEvent;
030: import java.awt.BorderLayout;
031: import java.awt.Color;
032: import java.awt.Dimension;
033: import java.awt.Font;
034: import java.awt.Graphics;
035: import java.awt.GridLayout;
036: import java.awt.Toolkit;
037: import java.awt.event.AWTEventListener;
038: import java.awt.event.ActionEvent;
039: import java.awt.event.ActionListener;
040: import java.awt.event.KeyEvent;
041: import java.awt.event.MouseAdapter;
042: import java.awt.event.MouseEvent;
043: import java.io.Serializable;
044: import java.text.SimpleDateFormat;
045: import java.util.Calendar;
046: import java.util.Date;
047: import java.util.GregorianCalendar;
048:
049: import javax.swing.ImageIcon;
050: import javax.swing.JButton;
051: import javax.swing.JComponent;
052: import javax.swing.JPanel;
053: import javax.swing.JSpinner;
054: import javax.swing.JSpinner.DateEditor;
055: import javax.swing.border.BevelBorder;
056: import javax.swing.event.EventListenerList;
057:
058: import br.com.gfpshare.beans.event.ICalendarEvent;
059: import br.com.gfpshare.beans.event.ICalendarEventListener;
060: import br.com.gfpshare.beans.event.ISpinnerModelEvent;
061: import br.com.gfpshare.beans.event.ISpinnerModelListener;
062:
063: /**
064: * Esta classe possui a capacidade de desenhar uma calendário na tela e de disparar eventos conforme o usuário
065: * interaje com este calendário.
066: *
067: * @author Administrador
068: * @created 20/01/2004
069: */
070: public class ZCalendarPanel extends JPanel implements Serializable,
071: ISpinnerModelListener {
072:
073: private JSpinner jsMes, jsAno;
074:
075: private JButton ok;
076:
077: private JPanel dias, mesEAno;
078:
079: private Font f = new Font("TimesRoman", Font.PLAIN, 12);
080:
081: private int diaAtual;
082:
083: private int monthLength[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30,
084: 31, 30, 31 };
085:
086: private String diasSemana[] = new String[7];;
087:
088: private DayCell[] cells;
089:
090: private EventListenerList listeners = new EventListenerList();
091:
092: /**
093: * Cria uma nova instância de JCalendarPanel
094: */
095: public ZCalendarPanel() {
096: Toolkit.getDefaultToolkit()
097: .addAWTEventListener(new ICalendarPanelEventProxy(),
098: AWTEvent.KEY_EVENT_MASK);
099:
100: setLayout(new BorderLayout());
101: loadDaysOfWeek();
102: setFont(f);
103: setBackground(new Color(224, 231, 239));
104:
105: Date hoje = new Date();
106: Calendar timeKeeper = Calendar.getInstance();
107: timeKeeper.add(Calendar.YEAR, -100);
108: Date dataMinima = timeKeeper.getTime();
109: timeKeeper.add(Calendar.YEAR, 1000);
110: Date dataMaxima = timeKeeper.getTime();
111: ISpinnerDateModel model = new ISpinnerDateModel(hoje,
112: dataMinima, dataMaxima, Calendar.YEAR);
113:
114: model.addModelListener(this );
115: jsAno = new JSpinner(model);
116: jsAno.setPreferredSize(new Dimension(50, 20));
117: jsAno.setEditor(new DateEditor(jsAno, "yyyy"));
118:
119: model = new ISpinnerDateModel(hoje, dataMinima, dataMaxima,
120: Calendar.MONTH);
121: model.addModelListener(this );
122: jsMes = new JSpinner(model);
123: jsMes.setPreferredSize(new Dimension(80, 20));
124: jsMes.setEditor(new DateEditor(jsMes, "MMMMM"));
125:
126: ok = new JButton(new ImageIcon(getClass().getResource(
127: "/icones/16x16/apply.png")));
128: ok.setPreferredSize(new Dimension(16, 16));
129: ok.setBorder(null);
130: OkListener listener = new OkListener();
131: ok.addMouseListener(listener);
132: ok.addActionListener(listener);
133:
134: mesEAno = new JPanel();
135: mesEAno.add(jsMes);
136: mesEAno.add(jsAno);
137: mesEAno.add(ok);
138:
139: add(mesEAno, "North");
140:
141: dias = new JPanel();
142: dias.setLayout(new GridLayout(0, 7));
143: add(dias, "Center");
144: setName("ICalendarPanel");
145:
146: timeKeeper = Calendar.getInstance();
147: diaAtual = timeKeeper.get(Calendar.DAY_OF_MONTH);
148: arrangeDays();
149: }
150:
151: /**
152: * Carrega os dias da semana localizados para o local atual
153: */
154: private void loadDaysOfWeek() {
155: Calendar diaSemana = Calendar.getInstance();
156: SimpleDateFormat formatter = new SimpleDateFormat("E");
157: for (int i = 0; i < diasSemana.length; i++) {
158: diaSemana.set(Calendar.DAY_OF_WEEK, i + 1);
159: diasSemana[i] = formatter.format(diaSemana.getTime());
160: }
161: }
162:
163: /**
164: * Retorna a data selecionada no calendário
165: *
166: * @return Data atualmente selecionada pelo calendário
167: */
168: public Date getDate() {
169: Calendar timeKeeper = Calendar.getInstance();
170: timeKeeper.set(getYear(), getMonth(), getDay());
171: return timeKeeper.getTime();
172: }
173:
174: /**
175: * Determina a data selecionada no calendário
176: *
177: * @param newDate Nova data selecionada pelo calendário
178: */
179: public void setDate(Date newDate) {
180: Calendar timeKeeper = Calendar.getInstance();
181: timeKeeper.setTime(newDate);
182: setYear(timeKeeper.get(Calendar.YEAR));
183: setMonth(timeKeeper.get(Calendar.MONTH));
184: setDay(timeKeeper.get(Calendar.DAY_OF_MONTH));
185: arrangeDays();
186: }
187:
188: /**
189: * Retorna o ano selecionado no calendário
190: *
191: * @return ano selecionado pelo calendário
192: */
193: public int getYear() {
194: Calendar timeKeeper = Calendar.getInstance();
195: timeKeeper.setTime((Date) jsAno.getValue());
196: return timeKeeper.get(Calendar.YEAR);
197:
198: }
199:
200: /**
201: * Determina o ano selecionado no calendário
202: *
203: * @param newYear novo selecionado pelo calendário
204: */
205: public void setYear(int newYear) {
206: Calendar timeKeeper = Calendar.getInstance();
207: timeKeeper.set(newYear, getMonth(), getDay());
208: jsAno.setValue(timeKeeper.getTime());
209:
210: (new Thread() {
211: @Override
212: public void run() {
213: fireAnoSelecionado();
214: }
215: }).start();
216: }
217:
218: /**
219: * Determina o mês selecionado no calendário
220: *
221: * @return mes selecionado pelo calendário
222: */
223: public int getMonth() {
224: Calendar timeKeeper = Calendar.getInstance();
225: timeKeeper.setTime((Date) jsMes.getValue());
226: return timeKeeper.get(Calendar.MONTH);
227: }
228:
229: /**
230: * Retorna o mês selecionado no calendário
231: *
232: * @param newMonth novo selecionado pelo calendário
233: */
234: public void setMonth(int newMonth) {
235: Calendar timeKeeper = Calendar.getInstance();
236: timeKeeper.set(getYear(), newMonth, 1);
237: if (timeKeeper.getActualMaximum(Calendar.DAY_OF_MONTH) >= getDay())
238: timeKeeper.set(getYear(), newMonth, getDay());
239: else
240: timeKeeper.set(getYear(), newMonth, timeKeeper
241: .getActualMaximum(Calendar.DAY_OF_MONTH));
242: jsMes.setValue(timeKeeper.getTime());
243:
244: (new Thread() {
245: @Override
246: public void run() {
247: fireMesSelecionado();
248: }
249: }).start();
250: }
251:
252: /**
253: * Retorna o dia selecionado no calendário
254: *
255: * @return dia selecionado pelo calendário
256: */
257: public int getDay() {
258: return diaAtual;
259: }
260:
261: /**
262: * Determina o dia selecionado no calendário
263: *
264: * @param newDay novo selecionado pelo calendário
265: */
266: public void setDay(int newDay) {
267: newDay = newDay < 1 ? 1 : newDay;
268: diaAtual = newDay;
269: if (cells != null)
270: arrangeDays();
271:
272: (new Thread() {
273: @Override
274: public void run() {
275: fireDiaSelecionado();
276: }
277: }).start();
278: }
279:
280: /**
281: * Retorna o calendário que determina data selecionada no calendário.
282: *
283: * @return Calendar que possui a data selecionada pelo calendário
284: */
285: public Calendar getCalendar() {
286: Calendar timeKeeper = Calendar.getInstance();
287: timeKeeper.set(getYear(), getMonth(), getDay());
288: return timeKeeper;
289: }
290:
291: /**
292: * Reorganiza todo o calendário de acordo com a data selecionada.
293: */
294: private void arrangeDays() {
295: Calendar hoje = Calendar.getInstance();
296: dias.removeAll();
297:
298: // MANOBRAS NECESSÁRIAS PARA SE PEGAR QUAL É O DIA DA SEMANA DO 1º DIA DE UM MÊS
299: int cMonth = getMonth();
300: int cYear = getYear();
301:
302: Calendar timeKeeper = Calendar.getInstance();
303: timeKeeper.set(cYear, cMonth, 1);
304: timeKeeper.setTime(timeKeeper.getTime());
305: int diaQueComeca = timeKeeper.get(Calendar.DAY_OF_WEEK);
306:
307: //JOGA FEVEREIRO PRA 29 DIAS EM UM ANO BISSEXTO
308: int max;
309: if ((cMonth == 1) && (timeKeeper instanceof GregorianCalendar)
310: && (((GregorianCalendar) timeKeeper).isLeapYear(cYear))) {
311: max = 29;
312: } else {
313: max = monthLength[cMonth];
314: }
315: if (diaAtual > max)
316: diaAtual = max;
317:
318: timeKeeper.set(cYear, cMonth, max);
319: timeKeeper.setTime(timeKeeper.getTime());
320: int diaQueTermina = timeKeeper.get(Calendar.DAY_OF_WEEK);
321:
322: //Adicionamos os dias da semana
323: for (int i = 0; i < 7; i++)
324: dias
325: .add(new WeekdayCell(diasSemana[i],
326: ZCalendarPanel.this ));
327:
328: /*
329: * Peenchemos com "espaços em branco" os primeiros dias Ex.: caso o mês comesse numa segunda então
330: * colocamos o domingo como vazio e a segunda como dia 1 terça como 2 e dai por diante.
331: */
332: for (int i = 1; i < diaQueComeca; i++)
333: dias.add(new WeekdayCell("", ZCalendarPanel.this ));
334:
335: //Peenchemos os dias do mês: 1,2,3,4,5,6,7......
336: cells = new DayCell[max];
337: for (int i = 0; i < max; i++) {
338: if (hoje.get(Calendar.YEAR) == timeKeeper
339: .get(Calendar.YEAR)
340: && hoje.get(Calendar.MONTH) == timeKeeper
341: .get(Calendar.MONTH)
342: && hoje.get(Calendar.DAY_OF_MONTH) == timeKeeper
343: .get(Calendar.DAY_OF_MONTH) + 1) {
344: cells[i] = new DayCell(i + 1, ZCalendarPanel.this , true);
345: } else {
346: cells[i] = new DayCell(i + 1, ZCalendarPanel.this ,
347: false);
348: }
349:
350: timeKeeper.set(cYear, cMonth, i + 1);
351: timeKeeper.setTime(timeKeeper.getTime());
352:
353: if (timeKeeper.get(Calendar.DAY_OF_WEEK) == 1) //Se for Domingo vai ser vermelho
354: cells[i].setForeground(new Color(255, 0, 0));
355: else if (timeKeeper.get(Calendar.DAY_OF_WEEK) == 7) //Se for Sábado vai ser esverdeado
356: cells[i].setForeground(new Color(0, 155, 0));
357:
358: dias.add(cells[i]);
359: }
360:
361: for (int i = diaQueTermina; i < 7; i++)
362: dias.add(new WeekdayCell("", ZCalendarPanel.this ));
363:
364: //Ativamos o dia atualmente selecionado
365: cells[diaAtual - 1].activate();
366:
367: dias.validate();
368: }
369:
370: /**
371: * Trata o evento de quando um dado é modificado nos JSpinner´s que compoem este componente
372: *
373: * @param e Evento do SpinnerDateModel
374: */
375: public void previoValorSelecionado(ISpinnerModelEvent e) {
376: if (e.getSource() == jsAno)
377: setYear(getYear());
378: else
379: setMonth(getMonth());
380: arrangeDays();
381: }
382:
383: /**
384: * Trata o evento de quando um dado é modificado nos JSpinner´s que compoem este componente
385: *
386: * @param e Evento do SpinnerDateModel
387: */
388: public void proximoValorSelecionado(ISpinnerModelEvent e) {
389: if (e.getSource() == jsAno)
390: setYear(getYear());
391: else
392: setMonth(getMonth());
393: arrangeDays();
394: }
395:
396: /**
397: * Adiciona um listener para trtatar os eventos do calendário
398: *
399: * @param listener Novo listener a ser adicionado
400: */
401: public void addCalendarListener(ICalendarEventListener listener) {
402: listeners.add(ICalendarEventListener.class, listener);
403: }
404:
405: /**
406: * Remove um listener da lista de listeners do calendário
407: *
408: * @param listener listener a ser removido
409: */
410: public void removeCalendarListener(ICalendarEventListener listener) {
411: listeners.remove(ICalendarEventListener.class, listener);
412: }
413:
414: /**
415: * Notifica todos os listeners que o calendário está abrindo
416: */
417: @SuppressWarnings("unused")
418: private void fireAbrindoCalendario() {
419: Object[] listeners = this .listeners
420: .getListeners(ICalendarEventListener.class);
421:
422: for (int i = 0; i < listeners.length; i++)
423: ((ICalendarEventListener) listeners[i])
424: .abrindoCalendario(new ICalendarEvent(this ));
425: }
426:
427: /**
428: * Notifica todos os listeners que o calendário está fechando
429: */
430: private void fireFechandoCalendario() {
431: Object[] listeners = this .listeners
432: .getListeners(ICalendarEventListener.class);
433:
434: for (int i = 0; i < listeners.length; i++)
435: ((ICalendarEventListener) listeners[i])
436: .fechandoCalendario(new ICalendarEvent(this ));
437: }
438:
439: /**
440: * Notifica todos os listener que novo ano foi selecionado
441: */
442: private void fireAnoSelecionado() {
443: Object[] listeners = this .listeners
444: .getListeners(ICalendarEventListener.class);
445:
446: for (int i = 0; i < listeners.length; i++)
447: ((ICalendarEventListener) listeners[i])
448: .anoSelecionado(new ICalendarEvent(this ));
449: }
450:
451: /**
452: * Notifica todos os listener que novo mes foi selecionado
453: */
454: private void fireMesSelecionado() {
455: Object[] listeners = this .listeners
456: .getListeners(ICalendarEventListener.class);
457:
458: for (int i = 0; i < listeners.length; i++)
459: ((ICalendarEventListener) listeners[i])
460: .mesSelecionado(new ICalendarEvent(this ));
461: }
462:
463: /**
464: * Notifica todos os listener que novo dia foi selecionado
465: */
466: private void fireDiaSelecionado() {
467: Object[] listeners = this .listeners
468: .getListeners(ICalendarEventListener.class);
469:
470: for (int i = 0; i < listeners.length; i++)
471: ((ICalendarEventListener) listeners[i])
472: .diaSelecionado(new ICalendarEvent(this ));
473: }
474:
475: /*********************************************************************************************************
476: * Classe interna que trrata os eventos do botão "ok"
477: *
478: * @author Igor Regis da Silva Simões
479: ********************************************************************************************************/
480: private class OkListener extends MouseAdapter implements
481: ActionListener {
482: /**
483: * Formata o botão ok quando este ganha o foco do mouse
484: *
485: * @param e
486: */
487: @Override
488: public void mouseEntered(MouseEvent e) {
489: if (e.getSource() == ok)
490: ok.setBorder(new BevelBorder(BevelBorder.RAISED));
491: }
492:
493: /**
494: * Formata o botão ok quando este perde o foco do mouse
495: *
496: * @param e
497: */
498: @Override
499: public void mouseExited(MouseEvent e) {
500: if (e.getSource() == ok)
501: ok.setBorder(null);
502: }
503:
504: /**
505: * Formata o botão ok quando este é precionado pelo mouse
506: *
507: * @param e
508: */
509: @Override
510: public void mousePressed(MouseEvent e) {
511: if (e.getSource() == ok)
512: ok.setBorder(new BevelBorder(BevelBorder.LOWERED));
513: }
514:
515: /**
516: * Trata o evento de ActionPerformed do botão ok
517: *
518: * @param e
519: */
520: public void actionPerformed(ActionEvent e) {
521: if (e.getSource() == ok)
522: fireFechandoCalendario();
523: }
524: }
525:
526: /*********************************************************************************************************
527: * Classe Interna que formata a célula do dia da semana
528: *
529: * @author Igor Regis da Silva Simões
530: ********************************************************************************************************/
531: private class WeekdayCell extends JComponent implements
532: Serializable {
533: private String day = null;
534:
535: /**
536: * Cria um novo dia da semana (Segunda, Terça...)
537: *
538: * @param newDay
539: * @param calendario
540: */
541: public WeekdayCell(String newDay, ZCalendarPanel calendario) {
542: setFont(calendario.getFont());
543: setForeground(new Color(0, 0, 255));
544: day = newDay;
545: }
546:
547: /**
548: * @see javax.swing.JComponent#paint(java.awt.Graphics)
549: */
550: @Override
551: public void paint(Graphics g) {
552: g.drawString(day, 2, 10);
553: }
554: }
555:
556: /*********************************************************************************************************
557: * Classe interna que formata as células do dia do mês.
558: *
559: * @author Igor Regis da Silva Simões
560: ********************************************************************************************************/
561: private class DayCell extends JComponent implements Serializable {
562: private ZCalendarPanel calendario;
563:
564: private int day;
565:
566: private Color corFonte = null;
567:
568: private boolean selected = false;
569:
570: private boolean isToday = false;
571:
572: /**
573: * Cria uma nova célula responssável por exibir um dia
574: *
575: * @param newDay
576: * @param frame
577: * @param isToday
578: */
579: public DayCell(int newDay, ZCalendarPanel frame, boolean isToday) {
580: calendario = frame;
581: day = newDay;
582: this .isToday = isToday;
583: setFont(calendario.getFont());
584: addMouseListener(new Mouser());
585: }
586:
587: /**
588: * Formata a célula para seu modo ativado
589: */
590: public void activate() {
591: selected = true;
592: }
593:
594: /**
595: * Formata a célula para seu modo desativado
596: */
597: public void deactivate() {
598: selected = false;
599: }
600:
601: /**
602: * Formata a célula para seu modo de foco
603: *
604: * @param foco
605: */
606: public void configuraFoco(boolean foco) {
607: if (foco) {
608: corFonte = getForeground();
609: setForeground(Color.YELLOW);
610: } else {
611: setForeground(corFonte);
612: }
613: }
614:
615: /**
616: * @see java.awt.Container#paint(java.awt.Graphics)
617: */
618: @Override
619: public void paint(Graphics g) {
620: if (isToday) {
621: g.setColor(Color.ORANGE);
622: g.fillOval(0, 0, this .getWidth(), this .getHeight());
623: }
624:
625: super .paint(g);
626:
627: if (selected) {
628: Color c = g.getColor();
629: g.setColor(Color.RED);
630: g.drawOval(0, 0, 20, 15);
631: g.drawOval(1, 1, 20, 15);
632: g.setColor(c);
633: }
634:
635: if (getForeground().equals(Color.YELLOW))
636: g.setColor(Color.YELLOW);
637:
638: g.drawString("" + day, 5, 12);
639: }
640:
641: /*****************************************************************************************************
642: * Mouse Listener responsável por ouvir os eventos gerados pelos "dias"
643: *
644: * @author Igor Regis da Silva Simões
645: ****************************************************************************************************/
646: private class Mouser extends MouseAdapter {
647: /**
648: * Trata o evento de quando o dia é clicado
649: *
650: * @param e
651: */
652: @Override
653: public void mouseClicked(MouseEvent e) {
654: activate();
655: calendario.setDay(day);
656: }
657:
658: /**
659: * Trata o evento de quando o dia recebe o foco do mouse
660: *
661: * @param e
662: */
663: @Override
664: public void mouseEntered(MouseEvent e) {
665: ((DayCell) e.getSource()).configuraFoco(true);
666: }
667:
668: /**
669: * Trata o evento de quando o dia perde o foco do mouse
670: *
671: * @param e
672: */
673: @Override
674: public void mouseExited(MouseEvent e) {
675: ((DayCell) e.getSource()).configuraFoco(false);
676: }
677: }
678: }
679:
680: /**
681: * Esta classe ouve os eventos do teclado e aciona o calendário, para mudança de dia conforme nescessário.
682: */
683: private class ICalendarPanelEventProxy implements AWTEventListener {
684: /**
685: * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
686: */
687: public void eventDispatched(AWTEvent event) {
688: if (event instanceof KeyEvent
689: && ZCalendarPanel.this .getParent().getParent()
690: .getParent().isVisible()) {
691: if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_RIGHT
692: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
693: && event.getID() == KeyEvent.KEY_PRESSED)
694: ZCalendarPanel.this .setDay(ZCalendarPanel.this
695: .getDay() + 1);
696: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_LEFT
697: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
698: && event.getID() == KeyEvent.KEY_PRESSED)
699: ZCalendarPanel.this .setDay(ZCalendarPanel.this
700: .getDay() - 1);
701: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_UP
702: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
703: && event.getID() == KeyEvent.KEY_PRESSED)
704: ZCalendarPanel.this .setDay(ZCalendarPanel.this
705: .getDay() - 7);
706: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_DOWN
707: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
708: && event.getID() == KeyEvent.KEY_PRESSED)
709: ZCalendarPanel.this .setDay(ZCalendarPanel.this
710: .getDay() + 7);
711: }
712: }
713: }
714:
715: }
|