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.igor.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.FontMetrics;
035: import java.awt.Graphics;
036: import java.awt.GridLayout;
037: import java.awt.Toolkit;
038: import java.awt.event.AWTEventListener;
039: import java.awt.event.ActionEvent;
040: import java.awt.event.ActionListener;
041: import java.awt.event.KeyEvent;
042: import java.awt.event.MouseAdapter;
043: import java.awt.event.MouseEvent;
044: import java.io.Serializable;
045: import java.text.SimpleDateFormat;
046: import java.util.Calendar;
047: import java.util.Date;
048: import java.util.GregorianCalendar;
049:
050: import javax.swing.ImageIcon;
051: import javax.swing.JButton;
052: import javax.swing.JComponent;
053: import javax.swing.JPanel;
054: import javax.swing.JSpinner;
055: import javax.swing.JSpinner.DateEditor;
056: import javax.swing.border.BevelBorder;
057: import javax.swing.event.EventListenerList;
058:
059: import br.com.igor.beans.event.ICalendarEvent;
060: import br.com.igor.beans.event.ICalendarEventListener;
061: import br.com.igor.beans.event.ISpinnerModelEvent;
062: import br.com.igor.beans.event.ISpinnerModelListener;
063:
064: /**
065: * Esta classe possui a capacidade de desenhar uma calendário na tela e de disparar eventos conforme o usuário
066: * interaje com este calendário.
067: *
068: * @author Administrador
069: * @created 20/01/2004
070: */
071: public class ZCalendarPanel extends JPanel implements Serializable,
072: ISpinnerModelListener {
073:
074: private JSpinner jsMes, jsAno;
075:
076: private JButton ok;
077:
078: private JPanel dias, mesEAno;
079:
080: private Font f = new Font("TimesRoman", Font.PLAIN, 12);
081:
082: private FontMetrics fm;
083:
084: private int diaAtual;
085:
086: private int monthLength[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30,
087: 31, 30, 31 };
088:
089: private String diasSemana[] = new String[7];;
090:
091: private DayCell[] cells;
092:
093: private EventListenerList listeners = new EventListenerList();
094:
095: /**
096: * Cria uma nova instância de JCalendarPanel
097: */
098: public ZCalendarPanel() {
099: Toolkit.getDefaultToolkit()
100: .addAWTEventListener(new ICalendarPanelEventProxy(),
101: AWTEvent.KEY_EVENT_MASK);
102:
103: setLayout(new BorderLayout());
104: loadDaysOfWeek();
105: setFont(f);
106: setBackground(new Color(224, 231, 239));
107:
108: Date hoje = new Date();
109: Calendar timeKeeper = Calendar.getInstance();
110: timeKeeper.add(Calendar.YEAR, -100);
111: Date dataMinima = timeKeeper.getTime();
112: timeKeeper.add(Calendar.YEAR, 1000);
113: Date dataMaxima = timeKeeper.getTime();
114: ISpinnerDateModel model = new ISpinnerDateModel(hoje,
115: dataMinima, dataMaxima, Calendar.YEAR);
116:
117: model.addModelListener(this );
118: jsAno = new JSpinner(model);
119: jsAno.setPreferredSize(new Dimension(50, 20));
120: jsAno.setEditor(new DateEditor(jsAno, "yyyy"));
121:
122: model = new ISpinnerDateModel(hoje, dataMinima, dataMaxima,
123: Calendar.MONTH);
124: model.addModelListener(this );
125: jsMes = new JSpinner(model);
126: jsMes.setPreferredSize(new Dimension(80, 20));
127: jsMes.setEditor(new DateEditor(jsMes, "MMMMM"));
128:
129: ok = new JButton(new ImageIcon(getClass().getResource(
130: "/icones/16x16/apply.png")));
131: ok.setPreferredSize(new Dimension(16, 16));
132: ok.setBorder(null);
133: OkListener listener = new OkListener();
134: ok.addMouseListener(listener);
135: ok.addActionListener(listener);
136:
137: mesEAno = new JPanel();
138: mesEAno.add(jsMes);
139: mesEAno.add(jsAno);
140: mesEAno.add(ok);
141:
142: add(mesEAno, "North");
143:
144: dias = new JPanel();
145: dias.setLayout(new GridLayout(0, 7));
146: add(dias, "Center");
147: setName("ICalendarPanel");
148:
149: timeKeeper = Calendar.getInstance();
150: diaAtual = timeKeeper.get(Calendar.DAY_OF_MONTH);
151: arrangeDays();
152: }
153:
154: /**
155: * Carrega os dias da semana localizados para o local atual
156: */
157: private void loadDaysOfWeek() {
158: Calendar diaSemana = Calendar.getInstance();
159: SimpleDateFormat formatter = new SimpleDateFormat("E");
160: for (int i = 0; i < diasSemana.length; i++) {
161: diaSemana.set(Calendar.DAY_OF_WEEK, i + 1);
162: diasSemana[i] = formatter.format(diaSemana.getTime());
163: }
164: }
165:
166: /**
167: * Retorna a data selecionada no calendário
168: *
169: * @return Data atualmente selecionada pelo calendário
170: */
171: public Date getDate() {
172: Calendar timeKeeper = Calendar.getInstance();
173: timeKeeper.set(getYear(), getMonth(), getDay());
174: return timeKeeper.getTime();
175: }
176:
177: /**
178: * Determina a data selecionada no calendário
179: *
180: * @param newDate Nova data selecionada pelo calendário
181: */
182: public void setDate(Date newDate) {
183: Calendar timeKeeper = Calendar.getInstance();
184: timeKeeper.setTime(newDate);
185: setYear(timeKeeper.get(Calendar.YEAR));
186: setMonth(timeKeeper.get(Calendar.MONTH));
187: setDay(timeKeeper.get(Calendar.DAY_OF_MONTH));
188: arrangeDays();
189: }
190:
191: /**
192: * Retorna o ano selecionado no calendário
193: *
194: * @return ano selecionado pelo calendário
195: */
196: public int getYear() {
197: Calendar timeKeeper = Calendar.getInstance();
198: timeKeeper.setTime((Date) jsAno.getValue());
199: return timeKeeper.get(Calendar.YEAR);
200:
201: }
202:
203: /**
204: * Determina o ano selecionado no calendário
205: *
206: * @param newYear novo selecionado pelo calendário
207: */
208: public void setYear(int newYear) {
209: Calendar timeKeeper = Calendar.getInstance();
210: timeKeeper.set(newYear, getMonth(), getDay());
211: jsAno.setValue(timeKeeper.getTime());
212:
213: (new Thread() {
214: public void run() {
215: fireAnoSelecionado();
216: }
217: }).start();
218: }
219:
220: /**
221: * Determina o mês selecionado no calendário
222: *
223: * @return mes selecionado pelo calendário
224: */
225: public int getMonth() {
226: Calendar timeKeeper = Calendar.getInstance();
227: timeKeeper.setTime((Date) jsMes.getValue());
228: return timeKeeper.get(Calendar.MONTH);
229: }
230:
231: /**
232: * Retorna o mês selecionado no calendário
233: *
234: * @param newMonth novo selecionado pelo calendário
235: */
236: public void setMonth(int newMonth) {
237: Calendar timeKeeper = Calendar.getInstance();
238: timeKeeper.set(getYear(), newMonth, 1);
239: if (timeKeeper.getActualMaximum(Calendar.DAY_OF_MONTH) >= getDay())
240: timeKeeper.set(getYear(), newMonth, getDay());
241: else
242: timeKeeper.set(getYear(), newMonth, timeKeeper
243: .getActualMaximum(Calendar.DAY_OF_MONTH));
244: jsMes.setValue(timeKeeper.getTime());
245:
246: (new Thread() {
247: public void run() {
248: fireMesSelecionado();
249: }
250: }).start();
251: }
252:
253: /**
254: * Retorna o dia selecionado no calendário
255: *
256: * @return dia selecionado pelo calendário
257: */
258: public int getDay() {
259: return diaAtual;
260: }
261:
262: /**
263: * Determina o dia selecionado no calendário
264: *
265: * @param newDay novo selecionado pelo calendário
266: */
267: public void setDay(int newDay) {
268: newDay = newDay < 1 ? 1 : newDay;
269: diaAtual = newDay;
270: if (cells != null)
271: arrangeDays();
272:
273: (new Thread() {
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: private void fireAbrindoCalendario() {
418: Object[] listeners = this .listeners
419: .getListeners(ICalendarEventListener.class);
420:
421: for (int i = 0; i < listeners.length; i++)
422: ((ICalendarEventListener) listeners[i])
423: .abrindoCalendario(new ICalendarEvent(this ));
424: }
425:
426: /**
427: * Notifica todos os listeners que o calendário está fechando
428: */
429: private void fireFechandoCalendario() {
430: Object[] listeners = this .listeners
431: .getListeners(ICalendarEventListener.class);
432:
433: for (int i = 0; i < listeners.length; i++)
434: ((ICalendarEventListener) listeners[i])
435: .fechandoCalendario(new ICalendarEvent(this ));
436: }
437:
438: /**
439: * Notifica todos os listener que novo ano foi selecionado
440: */
441: private void fireAnoSelecionado() {
442: Object[] listeners = this .listeners
443: .getListeners(ICalendarEventListener.class);
444:
445: for (int i = 0; i < listeners.length; i++)
446: ((ICalendarEventListener) listeners[i])
447: .anoSelecionado(new ICalendarEvent(this ));
448: }
449:
450: /**
451: * Notifica todos os listener que novo mes foi selecionado
452: */
453: private void fireMesSelecionado() {
454: Object[] listeners = this .listeners
455: .getListeners(ICalendarEventListener.class);
456:
457: for (int i = 0; i < listeners.length; i++)
458: ((ICalendarEventListener) listeners[i])
459: .mesSelecionado(new ICalendarEvent(this ));
460: }
461:
462: /**
463: * Notifica todos os listener que novo dia foi selecionado
464: */
465: private void fireDiaSelecionado() {
466: Object[] listeners = this .listeners
467: .getListeners(ICalendarEventListener.class);
468:
469: for (int i = 0; i < listeners.length; i++)
470: ((ICalendarEventListener) listeners[i])
471: .diaSelecionado(new ICalendarEvent(this ));
472: }
473:
474: /*********************************************************************************************************
475: * Classe interna que trrata os eventos do botão "ok"
476: *
477: * @author Igor Regis da Silva Simões
478: ********************************************************************************************************/
479: private class OkListener extends MouseAdapter implements
480: ActionListener {
481: /**
482: * Formata o botão ok quando este ganha o foco do mouse
483: *
484: * @param e
485: */
486: public void mouseEntered(MouseEvent e) {
487: if (e.getSource() == ok)
488: ok.setBorder(new BevelBorder(BevelBorder.RAISED));
489: }
490:
491: /**
492: * Formata o botão ok quando este perde o foco do mouse
493: *
494: * @param e
495: */
496: public void mouseExited(MouseEvent e) {
497: if (e.getSource() == ok)
498: ok.setBorder(null);
499: }
500:
501: /**
502: * Formata o botão ok quando este é precionado pelo mouse
503: *
504: * @param e
505: */
506: public void mousePressed(MouseEvent e) {
507: if (e.getSource() == ok)
508: ok.setBorder(new BevelBorder(BevelBorder.LOWERED));
509: }
510:
511: /**
512: * Trata o evento de ActionPerformed do botão ok
513: *
514: * @param e
515: */
516: public void actionPerformed(ActionEvent e) {
517: if (e.getSource() == ok)
518: fireFechandoCalendario();
519: }
520: }
521:
522: /*********************************************************************************************************
523: * Classe Interna que formata a célula do dia da semana
524: *
525: * @author Igor Regis da Silva Simões
526: ********************************************************************************************************/
527: private class WeekdayCell extends JComponent implements
528: Serializable {
529: private String day = null;
530:
531: /**
532: * Cria um novo dia da semana (Segunda, Terça...)
533: *
534: * @param newDay
535: * @param calendario
536: */
537: public WeekdayCell(String newDay, ZCalendarPanel calendario) {
538: setFont(calendario.getFont());
539: setForeground(new Color(0, 0, 255));
540: day = newDay;
541: }
542:
543: /**
544: * @see javax.swing.JComponent#paint(java.awt.Graphics)
545: */
546: public void paint(Graphics g) {
547: g.drawString(day, 2, 10);
548: }
549: }
550:
551: /*********************************************************************************************************
552: * Classe interna que formata as células do dia do mês.
553: *
554: * @author Igor Regis da Silva Simões
555: ********************************************************************************************************/
556: private class DayCell extends JComponent implements Serializable {
557: private ZCalendarPanel calendario;
558:
559: private int day;
560:
561: private Color corFonte = null;
562:
563: private boolean selected = false;
564:
565: private boolean isToday = false;
566:
567: private Color verde = new Color(0, 155, 0);
568:
569: /**
570: * Cria uma nova célula responssável por exibir um dia
571: *
572: * @param newDay
573: * @param frame
574: * @param isToday
575: */
576: public DayCell(int newDay, ZCalendarPanel frame, boolean isToday) {
577: calendario = frame;
578: day = newDay;
579: this .isToday = isToday;
580: setFont(calendario.getFont());
581: addMouseListener(new Mouser());
582: }
583:
584: /**
585: * Formata a célula para seu modo ativado
586: */
587: public void activate() {
588: selected = true;
589: }
590:
591: /**
592: * Formata a célula para seu modo desativado
593: */
594: public void deactivate() {
595: selected = false;
596: }
597:
598: /**
599: * Formata a célula para seu modo de foco
600: *
601: * @param foco
602: */
603: public void configuraFoco(boolean foco) {
604: if (foco) {
605: corFonte = getForeground();
606: setForeground(Color.YELLOW);
607: } else {
608: setForeground(corFonte);
609: }
610: }
611:
612: /**
613: * @see java.awt.Container#paint(java.awt.Graphics)
614: */
615: public void paint(Graphics g) {
616: if (isToday) {
617: g.setColor(Color.ORANGE);
618: g.fillOval(0, 0, this .getWidth(), this .getHeight());
619: }
620:
621: super .paint(g);
622:
623: if (selected) {
624: Color c = g.getColor();
625: g.setColor(Color.RED);
626: g.drawOval(0, 0, 20, 15);
627: g.drawOval(1, 1, 20, 15);
628: g.setColor(c);
629: }
630:
631: if (getForeground().equals(Color.YELLOW))
632: g.setColor(Color.YELLOW);
633:
634: g.drawString("" + day, 5, 12);
635: }
636:
637: /*****************************************************************************************************
638: * Mouse Listener responsável por ouvir os eventos gerados pelos "dias"
639: *
640: * @author Igor Regis da Silva Simões
641: ****************************************************************************************************/
642: private class Mouser extends MouseAdapter {
643: /**
644: * Trata o evento de quando o dia é clicado
645: *
646: * @param e
647: */
648: public void mouseClicked(MouseEvent e) {
649: activate();
650: calendario.setDay(day);
651: }
652:
653: /**
654: * Trata o evento de quando o dia recebe o foco do mouse
655: *
656: * @param e
657: */
658: public void mouseEntered(MouseEvent e) {
659: ((DayCell) e.getSource()).configuraFoco(true);
660: }
661:
662: /**
663: * Trata o evento de quando o dia perde o foco do mouse
664: *
665: * @param e
666: */
667: public void mouseExited(MouseEvent e) {
668: ((DayCell) e.getSource()).configuraFoco(false);
669: }
670: }
671: }
672:
673: /**
674: * Esta classe ouve os eventos do teclado e aciona o calendário, para mudança de dia conforme nescessário.
675: */
676: private class ICalendarPanelEventProxy implements AWTEventListener {
677: /**
678: * @see java.awt.event.AWTEventListener#eventDispatched(java.awt.AWTEvent)
679: */
680: public void eventDispatched(AWTEvent event) {
681: if (event instanceof KeyEvent
682: && ZCalendarPanel.this .getParent().getParent()
683: .getParent().isVisible()) {
684: if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_RIGHT
685: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
686: && event.getID() == KeyEvent.KEY_PRESSED)
687: ZCalendarPanel.this .setDay(ZCalendarPanel.this
688: .getDay() + 1);
689: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_LEFT
690: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
691: && event.getID() == KeyEvent.KEY_PRESSED)
692: ZCalendarPanel.this .setDay(ZCalendarPanel.this
693: .getDay() - 1);
694: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_UP
695: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
696: && event.getID() == KeyEvent.KEY_PRESSED)
697: ZCalendarPanel.this .setDay(ZCalendarPanel.this
698: .getDay() - 7);
699: else if (((KeyEvent) event).getKeyCode() == KeyEvent.VK_DOWN
700: && ((KeyEvent) event).getModifiers() == KeyEvent.CTRL_MASK
701: && event.getID() == KeyEvent.KEY_PRESSED)
702: ZCalendarPanel.this .setDay(ZCalendarPanel.this
703: .getDay() + 7);
704: }
705: }
706: }
707:
708: }
|