# HG changeset patch
# User František Kučera <franta-hg@frantovo.cz>
# Date 1335133471 -7200
# Node ID 9cb46ca7e26c0d2e18e2c34750143ddcd74a2d67
# Parent  290074b53acadeb354ca078c3a82b1537d3f26e1
#4 multipart/alternative zprávy: XHTML + prostý text (generuje se pomocí XSLT)

diff -r 290074b53aca -r 9cb46ca7e26c java/Postak/src/cz/frantovo/postak/HromadnaZprava.java
--- a/java/Postak/src/cz/frantovo/postak/HromadnaZprava.java	Fri Mar 30 15:35:26 2012 +0200
+++ b/java/Postak/src/cz/frantovo/postak/HromadnaZprava.java	Mon Apr 23 00:24:31 2012 +0200
@@ -9,130 +9,138 @@
 /**
  * Hromadná zpráva. Umí se rozdělit i na více dílčích zpráv,
  * které se vejdou do limitu příjemců SMTP serveru.
+ *
  * @author fiki
  */
 public class HromadnaZprava implements Cloneable {
 
-    private static final Logger log = Logger.getLogger(HromadnaZprava.class.getName());
-    /** Předmět zprávy */
-    private String predmet;
-    /** Adresa a případně jméno odesílatele */
-    private InternetAddress odesilatel;
-    /** Zda má být zpráva odeslána jako HTML nebo prostý text */
-    private boolean formatHTML = false;
-    /** Tělo zprávy - prostý text */
-    private String text;
-    /** Všichni příjemci této zprávy */
-    private ArrayList<InternetAddressKomu> prijemci = new ArrayList<InternetAddressKomu>();
-    /** Hlavička Reply-to */
-    private InternetAddress odpovedetKomu;
+	public enum FORMÁT {
 
-    public String getPredmet() {
-        return predmet;
-    }
+		XHTML,
+		PROSTÝ_TEXT,
+		OBOJE
+	}
+	private static final Logger log = Logger.getLogger(HromadnaZprava.class.getName());
+	/** Předmět zprávy */
+	private String predmet;
+	/** Adresa a případně jméno odesílatele */
+	private InternetAddress odesilatel;
+	/** Zda má být zpráva odeslána jako HTML nebo prostý text */
+	private FORMÁT formát = FORMÁT.PROSTÝ_TEXT;
+	/** Tělo zprávy - prostý text */
+	private String text;
+	/** Všichni příjemci této zprávy */
+	private ArrayList<InternetAddressKomu> prijemci = new ArrayList<InternetAddressKomu>();
+	/** Hlavička Reply-to */
+	private InternetAddress odpovedetKomu;
 
-    public void setPredmet(String predmet) {
-        this.predmet = predmet;
-    }
+	public String getPredmet() {
+		return predmet;
+	}
 
-    public InternetAddress getOdesilatel() {
-        return odesilatel;
-    }
+	public void setPredmet(String predmet) {
+		this.predmet = predmet;
+	}
 
-    public void setOdesilatel(InternetAddress odesilatel) {
-        this.odesilatel = odesilatel;
-    }
+	public InternetAddress getOdesilatel() {
+		return odesilatel;
+	}
 
-    public String getText() {
-        return text;
-    }
+	public void setOdesilatel(InternetAddress odesilatel) {
+		this.odesilatel = odesilatel;
+	}
 
-    public void setText(String text) {
-        this.text = text;
-    }
+	public String getText() {
+		return text;
+	}
 
-    public ArrayList<InternetAddressKomu> getPrijemci() {
-        return prijemci;
-    }
+	public void setText(String text) {
+		this.text = text;
+	}
 
-    /** 
-     * @param prijemci pokud je null, nastaví prázdná kolekce.
-     */
-    public void setPrijemci(ArrayList<InternetAddressKomu> prijemci) {
-        this.prijemci = new ArrayList<InternetAddressKomu>();
-        pridejPrijemce(prijemci);
-    }
+	public ArrayList<InternetAddressKomu> getPrijemci() {
+		return prijemci;
+	}
 
-    public void pridejPrijemce(Collection<InternetAddressKomu> prijemci) {
-        if (prijemci == null) {
-            log.log(Level.FINER, "Prázdná množina příjemců – nic nepřidáme");
-        } else {
-            this.prijemci.addAll(prijemci);
-        }
-    }
+	/**
+	 * @param prijemci pokud je null, nastaví prázdná kolekce.
+	 */
+	public void setPrijemci(ArrayList<InternetAddressKomu> prijemci) {
+		this.prijemci = new ArrayList<InternetAddressKomu>();
+		pridejPrijemce(prijemci);
+	}
 
-    /**
-     * Pokud má zpráva více přijemců, než je limit povolený SMTP serverem,
-     * rozdělí se zpráva na více dílčích zpráv.
-     * @param limit maximální počet příjemců jedné zprávy (omezení SMTP serveru)
-     */
-    public Collection<HromadnaZprava> getDilciZpravy(int limit) {
-        Collection<HromadnaZprava> zpravy = new ArrayList<HromadnaZprava>();
+	public void pridejPrijemce(Collection<InternetAddressKomu> prijemci) {
+		if (prijemci == null) {
+			log.log(Level.FINER, "Prázdná množina příjemců – nic nepřidáme");
+		} else {
+			this.prijemci.addAll(prijemci);
+		}
+	}
 
-        if (limit < getPrijemci().size()) {
-            /** Zprávu je potřeba rozdělit */
-            try {
-                HromadnaZprava dilciZprava = (HromadnaZprava) clone();
-                dilciZprava.setPrijemci(null);
+	/**
+	 * Pokud má zpráva více přijemců, než je limit povolený SMTP serverem,
+	 * rozdělí se zpráva na více dílčích zpráv.
+	 *
+	 * @param limit maximální počet příjemců jedné zprávy (omezení SMTP serveru)
+	 */
+	public Collection<HromadnaZprava> getDilciZpravy(int limit) {
+		Collection<HromadnaZprava> zpravy = new ArrayList<HromadnaZprava>();
 
-                ArrayList<InternetAddressKomu> vsichniPrijemci = getPrijemci();
-                for (InternetAddressKomu prijemce : vsichniPrijemci) {
-                    dilciZprava.getPrijemci().add(prijemce);
+		if (limit < getPrijemci().size()) {
+			/** Zprávu je potřeba rozdělit */
+			try {
+				HromadnaZprava dilciZprava = (HromadnaZprava) clone();
+				dilciZprava.setPrijemci(null);
 
-                    if (dilciZprava.getPrijemci().size() == limit) {
-                        zpravy.add(dilciZprava);
-                        dilciZprava = (HromadnaZprava) clone();
-                        dilciZprava.setPrijemci(null);
-                    }
-                }
+				ArrayList<InternetAddressKomu> vsichniPrijemci = getPrijemci();
+				for (InternetAddressKomu prijemce : vsichniPrijemci) {
+					dilciZprava.getPrijemci().add(prijemce);
 
-                if (dilciZprava.getPrijemci().size() > 0) {
-                    /**
-                     * Počet příjemců není násobkem limitu,
-                     * takže nakonci ještě někteří zbyli.
-                     * Z nich budou příjemci pro poslední dílčí zprávu.
-                     */
-                    zpravy.add(dilciZprava);
-                }
+					if (dilciZprava.getPrijemci().size() == limit) {
+						zpravy.add(dilciZprava);
+						dilciZprava = (HromadnaZprava) clone();
+						dilciZprava.setPrijemci(null);
+					}
+				}
 
+				if (dilciZprava.getPrijemci().size() > 0) {
+					/**
+					 * Počet příjemců není násobkem limitu,
+					 * takže nakonci ještě někteří zbyli.
+					 * Z nich budou příjemci pro poslední dílčí zprávu.
+					 */
+					zpravy.add(dilciZprava);
+				}
 
-            } catch (CloneNotSupportedException ex) {
-                /** Tohle by nikdy nemělo nastat */
-                log.log(Level.SEVERE, "Hromandou zprávu se nepodařilo naklonovat - divné.", ex);
-                zpravy.add(this);
-            }
-        } else {
-            /** Zprávu má málo příjemců - můžeme ji odeslat najednou */
-            zpravy.add(this);
-        }
 
-        return zpravy;
-    }
+			} catch (CloneNotSupportedException ex) {
+				/** Tohle by nikdy nemělo nastat */
+				log.log(Level.SEVERE, "Hromandou zprávu se nepodařilo naklonovat - divné.", ex);
+				zpravy.add(this);
+			}
+		} else {
+			/** Zprávu má málo příjemců - můžeme ji odeslat najednou */
+			zpravy.add(this);
+		}
 
-    public boolean isFormatHTML() {
-        return formatHTML;
-    }
+		return zpravy;
+	}
 
-    public void setFormatHTML(boolean formatHTML) {
-        this.formatHTML = formatHTML;
-    }
+	public FORMÁT getFormát() {
+		return formát;
+	}
 
-    public InternetAddress getOdpovedetKomu() {
-        return odpovedetKomu;
-    }
+	public void setFormatHTML(FORMÁT formát) {
+		this.formát = formát;
+	}
 
-    /** Pokud je null, hlavička Reply-to se v e-mailu vůbec nenastaví. */
-    public void setOdpovedetKomu(InternetAddress odpovedetKomu) {
-        this.odpovedetKomu = odpovedetKomu;
-    }
+	public InternetAddress getOdpovedetKomu() {
+		return odpovedetKomu;
+	}
+
+	/** Pokud je null, hlavička Reply-to se v e-mailu vůbec nenastaví. */
+	public void setOdpovedetKomu(InternetAddress odpovedetKomu) {
+		this.odpovedetKomu = odpovedetKomu;
+	}
 }
diff -r 290074b53aca -r 9cb46ca7e26c java/Postak/src/cz/frantovo/postak/Postak.java
--- a/java/Postak/src/cz/frantovo/postak/Postak.java	Fri Mar 30 15:35:26 2012 +0200
+++ b/java/Postak/src/cz/frantovo/postak/Postak.java	Mon Apr 23 00:24:31 2012 +0200
@@ -1,6 +1,8 @@
 package cz.frantovo.postak;
 
 import java.io.File;
+import java.io.StringReader;
+import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Properties;
@@ -13,210 +15,251 @@
 import javax.mail.PasswordAuthentication;
 import javax.mail.Session;
 import javax.mail.Transport;
+import javax.mail.internet.MimeBodyPart;
 import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
 
 /**
  * Odešle hromadnou zprávu pomocí SMTP.
- * 
+ *
  * @author fiki
  */
-public class Postak {   
-    
-    private static final Logger log = Logger.getLogger(Postak.class.getName());
-    /** Regulární výraz pro správnou e-mailovou adresu */
-    private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$";
-    
-    private Nastaveni nastaveni;
+public class Postak {
 
-    public Postak(Nastaveni nastaveni) {
-        this.nastaveni = nastaveni;
-    }
+	private static final String KÓDOVÁNÍ = "UTF-8";
+	private static final Logger log = Logger.getLogger(Postak.class.getName());
+	/** Regulární výraz pro správnou e-mailovou adresu */
+	private static String REGULARNI_EMAIL = "^[_a-zA-Z0-9\\.\\-]+@[_a-zA-Z0-9\\.\\-]+\\.[a-zA-Z]{2,4}$";
+	private Nastaveni nastaveni;
+	private TransformerFactory transformerFactory;
 
-    public void setNastaveni(Nastaveni nastaveni) {
-        this.nastaveni = nastaveni;
-    }
+	public Postak(Nastaveni nastaveni) {
+		this.nastaveni = nastaveni;
+		transformerFactory = TransformerFactory.newInstance();
+	}
 
-    /** 
-     * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců.
-     * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku.
-     * 
-     * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru
-     */
-    private void odesliSMTP(HromadnaZprava zprava) throws MessagingException {
+	public void setNastaveni(Nastaveni nastaveni) {
+		this.nastaveni = nastaveni;
+	}
 
-        if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) {
+	/**
+	 * Nízkoúrovňová odesílací metoda, která už nekontroluje limit příjemců.
+	 * Pokud se nevejdou do limitu SMTP serveru, vyhazuje výjimku.
+	 *
+	 * TODO: lepší to bude nestaické - nastavení si vyžádat v konstruktoru
+	 */
+	private void odesliSMTP(HromadnaZprava zprava) throws MessagingException {
 
-            /** Inicializace SMTP */
-            Properties smtpVlastnosti = System.getProperties();
-            smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer());
-            smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort()));
+		if (zkontrolujZpravu(zprava) && zkontrolujNastaveni(nastaveni)) {
 
-            if (nastaveni.getPostovniPort() == 465) {
-                if (new File(nastaveni.getCestaKCertifikatum()).exists()) {
-                    System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum());
-                    log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum());
-                }
-                /** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */
-                smtpVlastnosti.put("mail.smtp.starttls.enable", "true");
-                smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
-                smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort()));
-                smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false");
-            /**
-             * NAHRÁNÍ CERTIFIKÁTU:
-             * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer             
-             * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore -storepass "changeit"
-             * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty uložené v systému (Javovské).
-             */
-            }
+			/** Inicializace SMTP */
+			Properties smtpVlastnosti = System.getProperties();
+			smtpVlastnosti.put("mail.smtp.host", nastaveni.getPostovniServer());
+			smtpVlastnosti.put("mail.smtp.port", String.valueOf(nastaveni.getPostovniPort()));
 
-            PostakuvHeslovnik heslovnik = new PostakuvHeslovnik();
-            if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) {
-                heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo());
-                smtpVlastnosti.put("mail.smtp.auth", "true");
-                log.log(Level.FINEST, "Používám pro SMTP jméno a heslo");
-            }
+			if (nastaveni.getPostovniPort() == 465) {
+				if (new File(nastaveni.getCestaKCertifikatum()).exists()) {
+					System.setProperty("javax.net.ssl.trustStore", nastaveni.getCestaKCertifikatum());
+					log.log(Level.INFO, "Používám vlastní důvěryhodné certifikáty: {0}", nastaveni.getCestaKCertifikatum());
+				}
+				/** TODO: neřídit se číslem portu, ale přidat příznak pro šifrování */
+				smtpVlastnosti.put("mail.smtp.starttls.enable", "true");
+				smtpVlastnosti.put("mail.smtp.socketFactory.class", "javax.net.ssl.SSLSocketFactory");
+				smtpVlastnosti.put("mail.smtp.socketFactory.port", String.valueOf(nastaveni.getPostovniPort()));
+				smtpVlastnosti.put("mail.smtp.socketFactory.fallback", "false");
+				/**
+				 * NAHRÁNÍ CERTIFIKÁTU:
+				 * 1) stáhneme si certifikát (---BEGIN CERTIFICATE---) a uložíme do vse_ca.cer
+				 * 2) keytool -importcert -file vse_ca.cer -keystore DuveryhodneCertifikaty.keystore
+				 * -storepass "changeit"
+				 * Pokud daný soubor existuje, program ho používá, pokud ne, používá certifikáty
+				 * uložené v systému (Javovské).
+				 */
+			}
 
-            Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik);
+			PostakuvHeslovnik heslovnik = new PostakuvHeslovnik();
+			if (nastaveni.getPostovniJmeno() != null && nastaveni.getPostovniJmeno().length() > 0) {
+				heslovnik.setJmenoHeslo(nastaveni.getPostovniJmeno(), nastaveni.getPostovniHeslo());
+				smtpVlastnosti.put("mail.smtp.auth", "true");
+				log.log(Level.FINEST, "Používám pro SMTP jméno a heslo");
+			}
 
-            smtpRelace.setDebug(true);
-            smtpRelace.setDebugOut(System.out);
+			Session smtpRelace = Session.getInstance(smtpVlastnosti, heslovnik);
 
-            /** Sestavení zprávy */
-            MimeMessage mimeZprava = new MimeMessage(smtpRelace);
-            mimeZprava.setFrom(zprava.getOdesilatel());
-            if (zprava.getOdpovedetKomu() != null) {
-                Address[] odpovedetKomu = {zprava.getOdpovedetKomu()};
-                mimeZprava.setReplyTo(odpovedetKomu);
-            }
-            naplnPrijemce(mimeZprava, zprava);
-            mimeZprava.setSubject(zprava.getPredmet());
-            mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/");
-            mimeZprava.setHeader("Precedence", "bulk");
-            if (zprava.isFormatHTML()) {
-                mimeZprava.setText(zprava.getText(), "UTF-8", "html");
-            } else {
-                mimeZprava.setText(zprava.getText(), "UTF-8");
-            }
+			smtpRelace.setDebug(true);
+			smtpRelace.setDebugOut(System.out);
 
-            /** Vlastní odeslání */
-            Transport.send(mimeZprava);
+			/** Sestavení zprávy */
+			MimeMessage mimeZprava = new MimeMessage(smtpRelace);
+			mimeZprava.setFrom(zprava.getOdesilatel());
+			if (zprava.getOdpovedetKomu() != null) {
+				Address[] odpovedetKomu = {zprava.getOdpovedetKomu()};
+				mimeZprava.setReplyTo(odpovedetKomu);
+			}
+			naplnPrijemce(mimeZprava, zprava);
+			mimeZprava.setSubject(zprava.getPredmet());
+			mimeZprava.setHeader("User-Agent", "https://frantovo.cz/projekty/SuperPostak/");
+			mimeZprava.setHeader("Precedence", "bulk");
 
-        } else {
-            MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující");
-            log.log(Level.SEVERE, null, e);
-            throw e;
-        }
+			switch (zprava.getFormát()) {
+				case PROSTÝ_TEXT:
+					mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ);
+					break;
+				case XHTML:
+					mimeZprava.setText(zprava.getText(), KÓDOVÁNÍ, "html");
+					break;
+				case OBOJE:
+					MimeMultipart multipart = new MimeMultipart("alternative");
+					mimeZprava.setContent(multipart);
 
-    }
+					MimeBodyPart textováČást = new MimeBodyPart();
+					MimeBodyPart xhtmlČást = new MimeBodyPart();
 
-    /**
-     * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení.
-     * Respektuje typy příjemců: komu, kopie, skrytá kopie.    
-     */
-    private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException {
-        /** 
-         * Příjemci se budou definovat pouze ve zprávě.
-         * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou).
-         */
-        ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci();
-        for (InternetAddressKomu komu : prijemci) {
-            if (zkontrolujAdresu(komu)) {
-                mimeZprava.addRecipient(komu.getTyp(), komu);
-            }
-        }
-    }
+					textováČást.setText(xhtmlNaProstýText(zprava.getText()), KÓDOVÁNÍ);
+					xhtmlČást.setContent(zprava.getText(), "text/html; charset=" + KÓDOVÁNÍ);
 
-    /** Vypíše do logu seznam příjemců */
-    public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) {
-        for (InternetAddressKomu p : prijemci) {
-            log.log(Level.INFO, p.toString());
-        }
-    }
+					multipart.addBodyPart(textováČást);
+					multipart.addBodyPart(xhtmlČást);
+					break;
+			}
 
-    /** Veřejná odesílací metoda.
-     * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců)
-     */
-    public void odesli(HromadnaZprava zprava) throws MessagingException {
-        Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav());
+			/** Vlastní odeslání */
+			Transport.send(mimeZprava);
 
-        for (HromadnaZprava dilciZprava : zpravy) {
-            odesliSMTP(dilciZprava);
-        }
-    }
+		} else {
+			MessagingException e = new MessagingException("Zpráva nebo nastavení jsou nevyhovující");
+			log.log(Level.SEVERE, null, e);
+			throw e;
+		}
 
-    private static boolean zkontrolujAdresu(InternetAddressKomu a) {
-        if (a.getTyp() == null) {
-            log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress());
-            return false;
-        }
+	}
 
-        if (a.getAddress() == null || a.getAddress().length() < 1) {
-            log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal());
-            return false;
-        }
+	private String xhtmlNaProstýText(String xhtml) throws MessagingException {
+		try {
+			Transformer textTransformer = transformerFactory.newTransformer(new StreamSource(getClass().getClassLoader().getResourceAsStream("cz/frantovo/postak/odchozíEmailText.xsl")));
 
-        if (!zkontrolujAdresu(a.getAddress())) {
-            log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress());
-            return false;
-        }
+			StringReader input = new StringReader(xhtml);
+			StringWriter output = new StringWriter(xhtml.length());
+			textTransformer.transform(new StreamSource(input), new StreamResult(output));
 
-        return true;
-    }
+			return output.toString();
+		} catch (Exception e) {
+			throw new MessagingException("Chyba při XSL transformaci zprávy na prostý text.", e);
+		}
+	}
 
-    /** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */
-    public static boolean zkontrolujAdresu(String adresa) {
-        return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa);
-    }
+	/**
+	 * Nastaví zprávě (MimeMessage) všechny příjemce, které najde ve zprávě a nastavení.
+	 * Respektuje typy příjemců: komu, kopie, skrytá kopie.
+	 */
+	private static void naplnPrijemce(MimeMessage mimeZprava, HromadnaZprava zprava) throws MessagingException {
+		/**
+		 * Příjemci se budou definovat pouze ve zprávě.
+		 * Z nastavení se brát nebudou (výchozí příjemci už ve zprávě budou).
+		 */
+		ArrayList<InternetAddressKomu> prijemci = zprava.getPrijemci();
+		for (InternetAddressKomu komu : prijemci) {
+			if (zkontrolujAdresu(komu)) {
+				mimeZprava.addRecipient(komu.getTyp(), komu);
+			}
+		}
+	}
 
-    /** @return  Vrací true, pokud je zpráva v pořádku */
-    private static boolean zkontrolujZpravu(HromadnaZprava z) {
+	/** Vypíše do logu seznam příjemců */
+	public static void vypisPrijemce(Collection<InternetAddressKomu> prijemci) {
+		for (InternetAddressKomu p : prijemci) {
+			log.log(Level.INFO, p.toString());
+		}
+	}
 
-        if (z.getPrijemci() == null) {
-            log.log(Level.WARNING, "getPrijemci() == null");
-            return false;
-        }
+	/** Veřejná odesílací metoda.
+	 * Postará se o rozdělení zpráv na dílčí (které se vejdou do limitu příjemců)
+	 */
+	public void odesli(HromadnaZprava zprava) throws MessagingException {
+		Collection<HromadnaZprava> zpravy = zprava.getDilciZpravy(nastaveni.getLimitZprav());
 
-        if (z.getPrijemci().size() < 1) {
-            log.log(Level.WARNING, "getPrijemci().size() < 1");
-            return false;
-        }
+		for (HromadnaZprava dilciZprava : zpravy) {
+			odesliSMTP(dilciZprava);
+		}
+	}
 
-        if (z.getOdesilatel() == null) {
-            log.log(Level.WARNING, "getOdesilatel() == null");
-            return false;
-        }
+	private static boolean zkontrolujAdresu(InternetAddressKomu a) {
+		if (a.getTyp() == null) {
+			log.log(Level.WARNING, "Neplatná adresa (typ): {0}", a.getAddress());
+			return false;
+		}
 
-        if (z.getPredmet() == null) {
-            log.log(Level.WARNING, "getPredmet() == null");
-            return false;
-        }
+		if (a.getAddress() == null || a.getAddress().length() < 1) {
+			log.log(Level.WARNING, "Neplatná adresa (address): {0}", a.getPersonal());
+			return false;
+		}
 
-        return true;
-    }
+		if (!zkontrolujAdresu(a.getAddress())) {
+			log.log(Level.WARNING, "Adresa nevyhovuje regulárnímu výrazu: {0}", a.getAddress());
+			return false;
+		}
 
-    private static boolean zkontrolujNastaveni(Nastaveni n) {
+		return true;
+	}
 
-        if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) {
-            return false;
-        }
+	/** Zkontroluje e-mailovou adresu pomocí regulárního výrazu. */
+	public static boolean zkontrolujAdresu(String adresa) {
+		return adresa != null && Pattern.matches(REGULARNI_EMAIL, adresa);
+	}
 
-        return true;
-    }
+	/** @return Vrací true, pokud je zpráva v pořádku */
+	private static boolean zkontrolujZpravu(HromadnaZprava z) {
 
-    /** Slouží k uložení jména a hesla pro SMTP */
-    private static class PostakuvHeslovnik extends Authenticator {
+		if (z.getPrijemci() == null) {
+			log.log(Level.WARNING, "getPrijemci() == null");
+			return false;
+		}
 
-        private String jmeno = "user";
-        private char[] heslo = "luser".toCharArray();
+		if (z.getPrijemci().size() < 1) {
+			log.log(Level.WARNING, "getPrijemci().size() < 1");
+			return false;
+		}
 
-        @Override
-        public PasswordAuthentication getPasswordAuthentication() {
-            return new PasswordAuthentication(jmeno, String.valueOf(heslo));
-        }
+		if (z.getOdesilatel() == null) {
+			log.log(Level.WARNING, "getOdesilatel() == null");
+			return false;
+		}
 
-        public void setJmenoHeslo(String jmeno, char[] heslo) {
-            this.jmeno = jmeno;
-            this.heslo = heslo;
-        }
-    }
+		if (z.getPredmet() == null) {
+			log.log(Level.WARNING, "getPredmet() == null");
+			return false;
+		}
+
+		return true;
+	}
+
+	private static boolean zkontrolujNastaveni(Nastaveni n) {
+
+		if (n.getPostovniServer() == null || n.getPostovniServer().length() < 1) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/** Slouží k uložení jména a hesla pro SMTP */
+	private static class PostakuvHeslovnik extends Authenticator {
+
+		private String jmeno = "user";
+		private char[] heslo = "luser".toCharArray();
+
+		@Override
+		public PasswordAuthentication getPasswordAuthentication() {
+			return new PasswordAuthentication(jmeno, String.valueOf(heslo));
+		}
+
+		public void setJmenoHeslo(String jmeno, char[] heslo) {
+			this.jmeno = jmeno;
+			this.heslo = heslo;
+		}
+	}
 }
diff -r 290074b53aca -r 9cb46ca7e26c java/Postak/src/cz/frantovo/postak/odchozíEmailText.xsl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/Postak/src/cz/frantovo/postak/odchozíEmailText.xsl	Mon Apr 23 00:24:31 2012 +0200
@@ -0,0 +1,274 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	Převzato z projektu hellDesk.
+-->
+<xsl:stylesheet version="1.0"
+				xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+				xmlns:fn="http://www.w3.org/2005/xpath-functions"
+				xmlns:str="http://xsltsl.org/string"
+				xmlns:h="http://www.w3.org/1999/xhtml">
+	<xsl:output method="text" encoding="UTF-8"/>
+	<xsl:strip-space elements="*"/>
+
+	<xsl:output method="text" encoding="UTF-8"/>
+	<xsl:strip-space elements="*"/>
+
+	<xsl:variable name="urlBase" select="/h:html/h:head/h:base/@href"/>
+
+
+	<!-- Celý dokument -->
+	<xsl:template match="/">
+		<xsl:apply-templates select="h:html/h:body"/>
+	</xsl:template>
+
+
+	<xsl:template match="h:h1">
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:call-template name="str:generate-string">
+			<xsl:with-param name="text" select="'='"/>
+			<xsl:with-param name="count" select="string-length(.)"/>
+		</xsl:call-template>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:h2">
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:call-template name="str:generate-string">
+			<xsl:with-param name="text" select="'-'"/>
+			<xsl:with-param name="count" select="string-length(.)"/>
+		</xsl:call-template>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:h3">
+		<xsl:text>### </xsl:text>
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:h4">
+		<xsl:text>#### </xsl:text>
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:h5">
+		<xsl:text>##### </xsl:text>
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:h6">
+		<xsl:text>###### </xsl:text>
+		<xsl:value-of select="text()"/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:p">
+		<xsl:apply-templates/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+	
+	<xsl:template match="h:blockquote/h:p">
+		<xsl:text>&gt; </xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:cite[not(starts-with(., '„') or starts-with(., '‚'))]">
+		<xsl:text>„</xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>“</xsl:text>
+	</xsl:template>	
+
+
+	<xsl:template match="h:a">
+		<xsl:if test="not(text() = @href)">
+			<xsl:text>'</xsl:text>
+			<xsl:apply-templates select="node()"/>
+			<xsl:text>' </xsl:text>
+		</xsl:if>
+		<xsl:text>&lt;</xsl:text>
+		<xsl:choose>
+			<xsl:when test="starts-with(@href, 'http:') or starts-with(@href, 'https:') or starts-with(@href, 'ftp:')">
+				<xsl:value-of select="@href"/>
+			</xsl:when>
+			<xsl:when test="starts-with(@href, 'mailto:')">
+				<xsl:value-of select="substring-after(@href, 'mailto:')"/>
+			</xsl:when>
+			<xsl:otherwise>
+				<xsl:choose>
+					<!-- TODO: $urlBase by nemělo končit lomítkem -->
+					<xsl:when test="starts-with(@href, '/')">
+						<xsl:value-of select="concat($urlBase, @href)"/>
+					</xsl:when>
+					<xsl:otherwise>
+						<xsl:value-of select="concat($urlBase, '/', @href)"/>
+					</xsl:otherwise>
+				</xsl:choose>
+			</xsl:otherwise>
+		</xsl:choose>
+		<xsl:text>&gt;</xsl:text>
+		<xsl:if test="normalize-space(@title)">
+			<xsl:text> (</xsl:text>
+			<xsl:value-of select="@title"/>
+			<xsl:text>)</xsl:text>
+		</xsl:if>
+	</xsl:template>
+
+
+	<xsl:template match="h:img">
+		<xsl:text>Obrázek: </xsl:text>
+		<xsl:value-of select="@src"/>
+		<!-- TODO: relativní odkazy -->
+	</xsl:template>
+
+
+	<xsl:template match="h:strong|h:b">
+		<xsl:text>**</xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>**</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:em|h:i">
+		<xsl:text>*</xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>*</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:abbr[@title]">
+		<xsl:apply-templates/>
+		<xsl:text> (</xsl:text>
+		<xsl:value-of select="@title"/>
+		<xsl:text>)</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:pre">
+		<xsl:text>--------------------------------</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>--------------------------------</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:code">
+		<xsl:text>`</xsl:text>
+		<xsl:apply-templates/>
+		<xsl:text>`</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:hr">
+		<xsl:text>----------------------------------------------------------------</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>
+
+
+	<xsl:template match="h:br">
+		<xsl:text>&#10;</xsl:text>
+	</xsl:template>	
+
+
+	<xsl:template match="h:ol|h:ul">
+		<xsl:variable name="úroveň" select="count(ancestor::h:li)"/>
+		<xsl:variable name="odsazení">
+			<!-- položky na nejvyšší úrovni odsazené jednou mezerou -->
+			<xsl:value-of select="' '"/>
+			<xsl:call-template name="str:generate-string">
+				<xsl:with-param name="text" select="'  '"/>
+				<!-- položky na druhé a vyšší úrovni odsazené vždy dvěma dalšími mezerami -->
+				<xsl:with-param name="count" select="$úroveň - 1"/>
+			</xsl:call-template>
+		</xsl:variable>
+
+		<xsl:if test="$úroveň &gt; 0">
+			<xsl:text>&#10;</xsl:text>
+		</xsl:if>
+
+		<xsl:for-each select="h:li">
+			<xsl:if test="parent::h:ol">
+				<xsl:value-of select="concat($odsazení, position(), ') ')"/>
+			</xsl:if>
+			<xsl:if test="parent::h:ul">
+				<xsl:value-of select="concat($odsazení, '- ')"/>
+			</xsl:if>
+			<xsl:apply-templates/>
+			<xsl:if test="$úroveň = 0 or not(position() = last())">
+				<xsl:text>&#10;</xsl:text>
+			</xsl:if>
+		</xsl:for-each>
+		
+		<xsl:if test="$úroveň = 0">
+			<xsl:text>&#10;</xsl:text>
+		</xsl:if>
+	</xsl:template>
+
+
+	<xsl:template match="text()[not(parent::h:pre)]">
+		<xsl:if test="(starts-with(., ' ') or starts-with(., '&#10;')) and preceding-sibling::node() and not(preceding-sibling::h:br)">
+			<xsl:text> </xsl:text>
+		</xsl:if>
+		<xsl:value-of select="normalize-space(.)"/>
+		<xsl:variable name="posledníZnak" select="substring(., string-length(.))"/>
+		<!-- TODO: tabulátor? -->
+		<xsl:if test="($posledníZnak = ' ' or $posledníZnak = '&#10;') and following-sibling::node()">
+			<xsl:text> </xsl:text>
+		</xsl:if>
+	</xsl:template>
+	
+	
+	<xsl:template match="h:div[@class = 'patička']">
+		<xsl:text>-- </xsl:text>
+		<xsl:text>&#10;</xsl:text>
+		<xsl:apply-templates/>
+	</xsl:template>
+	
+
+	<!--
+		Převzato z:
+		http://xsltsl.sourceforge.net/string.html#template.str:generate-string
+	-->
+	<xsl:template name="str:generate-string">
+		<xsl:param name="text"/>
+		<xsl:param name="count"/>
+
+		<xsl:choose>
+			<xsl:when test="string-length($text) = 0 or $count &lt;= 0"/>
+			<xsl:otherwise>
+				<xsl:value-of select="$text"/>
+				<xsl:call-template name="str:generate-string">
+					<xsl:with-param name="text" select="$text"/>
+					<xsl:with-param name="count" select="$count - 1"/>
+				</xsl:call-template>
+			</xsl:otherwise>
+		</xsl:choose>
+	</xsl:template>
+
+
+</xsl:stylesheet>
+
diff -r 290074b53aca -r 9cb46ca7e26c java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.form
--- a/java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.form	Fri Mar 30 15:35:26 2012 +0200
+++ b/java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.form	Mon Apr 23 00:24:31 2012 +0200
@@ -510,8 +510,8 @@
           <Group type="103" groupAlignment="0" attributes="0">
               <Group type="102" alignment="1" attributes="0">
                   <EmptySpace max="-2" attributes="0"/>
-                  <Group type="103" groupAlignment="1" attributes="0">
-                      <Component id="jScrollPane1" alignment="0" pref="438" max="32767" attributes="0"/>
+                  <Group type="103" groupAlignment="0" attributes="0">
+                      <Component id="jScrollPane1" alignment="0" pref="461" max="32767" attributes="0"/>
                       <Group type="102" alignment="1" attributes="0">
                           <Group type="103" groupAlignment="1" attributes="0">
                               <Group type="102" attributes="0">
@@ -527,16 +527,20 @@
                               </Group>
                           </Group>
                           <Group type="103" groupAlignment="0" attributes="0">
-                              <Component id="vstupJmenoOdesilatele" alignment="0" pref="328" max="32767" attributes="0"/>
-                              <Component id="vstupAdresaOdesilatele" alignment="0" pref="328" max="32767" attributes="0"/>
-                              <Component id="vstupPredmet" alignment="0" pref="328" max="32767" attributes="0"/>
+                              <Component id="vstupJmenoOdesilatele" alignment="0" max="32767" attributes="0"/>
+                              <Component id="vstupAdresaOdesilatele" alignment="0" max="32767" attributes="0"/>
+                              <Component id="vstupPredmet" alignment="0" max="32767" attributes="0"/>
                           </Group>
                       </Group>
                       <Group type="102" alignment="0" attributes="0">
                           <Component id="jLabel15" min="-2" max="-2" attributes="0"/>
-                          <EmptySpace min="-2" pref="33" max="-2" attributes="0"/>
-                          <Component id="vstupFormatHTML" pref="81" max="32767" attributes="0"/>
-                          <EmptySpace min="-2" pref="247" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="formátProstý" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="formátXHTML" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace max="-2" attributes="0"/>
+                          <Component id="formátOboje" min="-2" max="-2" attributes="0"/>
+                          <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
                       </Group>
                   </Group>
                   <EmptySpace max="-2" attributes="0"/>
@@ -566,7 +570,9 @@
                   <EmptySpace max="-2" attributes="0"/>
                   <Group type="103" groupAlignment="3" attributes="0">
                       <Component id="jLabel15" alignment="3" min="-2" max="-2" attributes="0"/>
-                      <Component id="vstupFormatHTML" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="formátProstý" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="formátXHTML" alignment="3" min="-2" max="-2" attributes="0"/>
+                      <Component id="formátOboje" alignment="3" min="-2" max="-2" attributes="0"/>
                   </Group>
                   <EmptySpace max="-2" attributes="0"/>
               </Group>
@@ -616,13 +622,6 @@
             <Property name="text" type="java.lang.String" value="Form&#xe1;t zpr&#xe1;vy"/>
           </Properties>
         </Component>
-        <Component class="javax.swing.JCheckBox" name="vstupFormatHTML">
-          <Properties>
-            <Property name="mnemonic" type="int" value="104"/>
-            <Property name="text" type="java.lang.String" value="HTML"/>
-            <Property name="toolTipText" type="java.lang.String" value="P&#x159;i neza&#x161;krtnut&#xed; se ode&#x161;le jako prost&#xfd; text"/>
-          </Properties>
-        </Component>
         <Component class="org.jdesktop.swingx.JXHyperlink" name="odpovedetKomuOdkaz">
           <Properties>
             <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
@@ -642,6 +641,37 @@
             <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="odpovedetKomuOdkazActionPerformed"/>
           </Events>
         </Component>
+        <Component class="javax.swing.JRadioButton" name="form&#xe1;tProst&#xfd;">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="form&#xe1;tSkupina"/>
+            </Property>
+            <Property name="selected" type="boolean" value="true"/>
+            <Property name="text" type="java.lang.String" value="Prost&#xfd; text"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JRadioButton" name="form&#xe1;tXHTML">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="form&#xe1;tSkupina"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="(X)HTML"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="form&#xe1;tXHTMLActionPerformed"/>
+          </Events>
+        </Component>
+        <Component class="javax.swing.JRadioButton" name="form&#xe1;tOboje">
+          <Properties>
+            <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
+              <ComponentRef name="form&#xe1;tSkupina"/>
+            </Property>
+            <Property name="text" type="java.lang.String" value="Oboje"/>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="form&#xe1;tObojeActionPerformed"/>
+          </Events>
+        </Component>
       </SubComponents>
     </Container>
     <Container class="javax.swing.JDialog" name="odpovedetKomuDialog">
@@ -726,6 +756,8 @@
         </Component>
       </SubComponents>
     </Container>
+    <Component class="javax.swing.ButtonGroup" name="form&#xe1;tSkupina">
+    </Component>
   </NonVisualComponents>
   <Properties>
     <Property name="title" type="java.lang.String" value="SuperPo&#x161;&#x165;&#xe1;k"/>
diff -r 290074b53aca -r 9cb46ca7e26c java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.java
--- a/java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.java	Fri Mar 30 15:35:26 2012 +0200
+++ b/java/SuperPostak/src/cz/frantovo/superPostak/SuperPostak.java	Mon Apr 23 00:24:31 2012 +0200
@@ -9,6 +9,8 @@
 import java.awt.Cursor;
 import java.awt.Dialog.ModalityType;
 import java.awt.KeyboardFocusManager;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemListener;
 import java.beans.XMLDecoder;
 import java.beans.XMLEncoder;
 import java.io.BufferedInputStream;
@@ -28,19 +30,15 @@
 import java.util.zip.GZIPOutputStream;
 import javax.mail.MessagingException;
 import javax.mail.internet.InternetAddress;
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-import javax.swing.JOptionPane;
-import javax.swing.JTextArea;
-import javax.swing.UIManager;
-import javax.swing.UnsupportedLookAndFeelException;
+import javax.swing.*;
+import javax.swing.event.ChangeListener;
 import org.jdesktop.swingx.JXErrorPane;
 import org.jdesktop.swingx.JXTaskPaneContainer;
 import org.jdesktop.swingx.error.ErrorInfo;
 
 /**
  *
- * @author  fiki
+ * @author fiki
  */
 public class SuperPostak extends javax.swing.JFrame {
 
@@ -56,6 +54,7 @@
 
 	/**
 	 * Creates new form SuperPostak
+	 *
 	 * @param ladit předvyplní testovací hodnoty - zprávu
 	 * @param exit při zavření okna ukončí program
 	 */
@@ -350,8 +349,10 @@
         jScrollPane1 = new javax.swing.JScrollPane();
         vstupZprava = new javax.swing.JTextArea();
         jLabel15 = new javax.swing.JLabel();
-        vstupFormatHTML = new javax.swing.JCheckBox();
         odpovedetKomuOdkaz = new org.jdesktop.swingx.JXHyperlink();
+        formátProstý = new javax.swing.JRadioButton();
+        formátXHTML = new javax.swing.JRadioButton();
+        formátOboje = new javax.swing.JRadioButton();
         odpovedetKomuDialog = new javax.swing.JDialog();
         jLabel18 = new javax.swing.JLabel();
         jLabel19 = new javax.swing.JLabel();
@@ -359,6 +360,7 @@
         odpovedetKomuJmeno = new javax.swing.JTextField();
         odpovedetKomuAdresa = new javax.swing.JTextField();
         odpovedetKomuHotovo = new javax.swing.JButton();
+        formátSkupina = new javax.swing.ButtonGroup();
         jXHeader1 = new org.jdesktop.swingx.JXHeader();
         taskPanel = new org.jdesktop.swingx.JXTaskPaneContainer();
         jXTaskPane1 = new org.jdesktop.swingx.JXTaskPane();
@@ -700,10 +702,6 @@
 
         jLabel15.setText("Formát zprávy");
 
-        vstupFormatHTML.setMnemonic('h');
-        vstupFormatHTML.setText("HTML");
-        vstupFormatHTML.setToolTipText("Při nezaškrtnutí se odešle jako prostý text");
-
         odpovedetKomuOdkaz.setForeground(javax.swing.UIManager.getDefaults().getColor("windowText"));
         odpovedetKomuOdkaz.setMnemonic('e');
         odpovedetKomuOdkaz.setText("Adresa odesílatele");
@@ -716,15 +714,35 @@
             }
         });
 
+        formátSkupina.add(formátProstý);
+        formátProstý.setSelected(true);
+        formátProstý.setText("Prostý text");
+
+        formátSkupina.add(formátXHTML);
+        formátXHTML.setText("(X)HTML");
+        formátXHTML.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                formátXHTMLActionPerformed(evt);
+            }
+        });
+
+        formátSkupina.add(formátOboje);
+        formátOboje.setText("Oboje");
+        formátOboje.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                formátObojeActionPerformed(evt);
+            }
+        });
+
         javax.swing.GroupLayout panelZpravaLayout = new javax.swing.GroupLayout(panelZprava);
         panelZprava.setLayout(panelZpravaLayout);
         panelZpravaLayout.setHorizontalGroup(
             panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
             .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelZpravaLayout.createSequentialGroup()
                 .addContainerGap()
-                .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
-                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 438, Short.MAX_VALUE)
-                    .addGroup(panelZpravaLayout.createSequentialGroup()
+                .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+                    .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 461, Short.MAX_VALUE)
+                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, panelZpravaLayout.createSequentialGroup()
                         .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                             .addGroup(panelZpravaLayout.createSequentialGroup()
                                 .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@@ -735,14 +753,18 @@
                                 .addComponent(odpovedetKomuOdkaz, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)))
                         .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
-                            .addComponent(vstupJmenoOdesilatele, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE)
-                            .addComponent(vstupAdresaOdesilatele, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE)
-                            .addComponent(vstupPredmet, javax.swing.GroupLayout.DEFAULT_SIZE, 328, Short.MAX_VALUE)))
-                    .addGroup(javax.swing.GroupLayout.Alignment.LEADING, panelZpravaLayout.createSequentialGroup()
+                            .addComponent(vstupJmenoOdesilatele)
+                            .addComponent(vstupAdresaOdesilatele)
+                            .addComponent(vstupPredmet)))
+                    .addGroup(panelZpravaLayout.createSequentialGroup()
                         .addComponent(jLabel15)
-                        .addGap(33, 33, 33)
-                        .addComponent(vstupFormatHTML, javax.swing.GroupLayout.DEFAULT_SIZE, 81, Short.MAX_VALUE)
-                        .addGap(247, 247, 247)))
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(formátProstý)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(formátXHTML)
+                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+                        .addComponent(formátOboje)
+                        .addGap(0, 0, Short.MAX_VALUE)))
                 .addContainerGap())
         );
         panelZpravaLayout.setVerticalGroup(
@@ -765,7 +787,9 @@
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                 .addGroup(panelZpravaLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                     .addComponent(jLabel15)
-                    .addComponent(vstupFormatHTML))
+                    .addComponent(formátProstý)
+                    .addComponent(formátXHTML)
+                    .addComponent(formátOboje))
                 .addContainerGap())
         );
 
@@ -816,7 +840,7 @@
                     .addComponent(jLabel19)
                     .addComponent(odpovedetKomuAdresa, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
-                .addComponent(jLabel17)
+                .addComponent(jLabel17, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                 .addComponent(odpovedetKomuHotovo)
                 .addContainerGap())
@@ -966,7 +990,17 @@
 		zprava.setOdesilatel(new InternetAddress(vstupAdresaOdesilatele.getText(), vstupJmenoOdesilatele.getText()));
 		zprava.setPredmet(vstupPredmet.getText());
 		zprava.setText(vstupZprava.getText());
-		zprava.setFormatHTML(vstupFormatHTML.isSelected());
+
+		if (formátProstý.isSelected()) {
+			zprava.setFormatHTML(HromadnaZprava.FORMÁT.PROSTÝ_TEXT);
+		} else if (formátXHTML.isSelected()) {
+			zprava.setFormatHTML(HromadnaZprava.FORMÁT.XHTML);
+		} else if (formátOboje.isSelected()) {
+			zprava.setFormatHTML(HromadnaZprava.FORMÁT.OBOJE);
+		} else {
+			log.severe("Není zvolen formát zprávy → nastavuji: prostý text.");
+			zprava.setFormatHTML(HromadnaZprava.FORMÁT.PROSTÝ_TEXT);
+		}
 
 		if (Postak.zkontrolujAdresu(odpovedetKomuAdresa.getText())) {
 			InternetAddress komu = new InternetAddress();
@@ -1102,6 +1136,28 @@
 		}
 }//GEN-LAST:event_odpovedetKomuHotovoActionPerformed
 
+	private void formátXHTMLActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formátXHTMLActionPerformed
+		nastavVýchozíXHTML();
+	}//GEN-LAST:event_formátXHTMLActionPerformed
+
+	private void formátObojeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_formátObojeActionPerformed
+		nastavVýchozíXHTML();
+	}//GEN-LAST:event_formátObojeActionPerformed
+
+	/**
+	 * Pokud ještě není vyplněn žádný text, vložíme šablonu.
+	 */
+	private void nastavVýchozíXHTML() {
+		if (vstupZprava.getText().isEmpty()) {
+			vstupZprava.setText("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
+					+ "<body>\n\n"
+					+ "<p></p>\n"
+					+ "<div class=\"patička\"></div>\n\n"
+					+ "</body>\n"
+					+ "</html>");
+		}
+	}
+
 	/** Provede uživatelem zadaný SQL dotaz a vrátí získané příjemce */
 	private Collection<InternetAddressKomu> getPrijemciSQL() throws SQLException, UnsupportedEncodingException {
 		if ("".equals(vstupSQL.getText())) {
@@ -1183,6 +1239,10 @@
 		});
 	}
     // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JRadioButton formátOboje;
+    private javax.swing.JRadioButton formátProstý;
+    private javax.swing.ButtonGroup formátSkupina;
+    private javax.swing.JRadioButton formátXHTML;
     private javax.swing.JLabel jLabel1;
     private javax.swing.JLabel jLabel10;
     private javax.swing.JLabel jLabel11;
@@ -1239,7 +1299,6 @@
     private javax.swing.JPasswordField vstupDBheslo;
     private javax.swing.JTextField vstupDBjmeno;
     private javax.swing.JTextField vstupDatabaze;
-    private javax.swing.JCheckBox vstupFormatHTML;
     private javax.swing.JTextField vstupJmenoOdesilatele;
     private javax.swing.JTextField vstupPredmet;
     private javax.swing.JPasswordField vstupSMTPheslo;