Swingإنشاء برنامج لحفظ و عرض منتجات شركة
في هذا الدرس ستتعلم طريقة إنشاء برنامج لحفظ و عرض منتجات الشركة باستخدام إطار الـ Swing.
الأشياء المهمة التي ستتعلمها في هذا الدرس
- طريقة بناء قاعدة بيانات من الصفر.
- طريقة ربط البرنامج بقاعدة بيانات.
- طريقة إظهار محتوى قاعدة البيانات في جدول.
- طريقة إضافة, تحديث, مسح محتوى قاعدة البيانات من البرنامج.
- طريقة البحث السريع في الجدول.
- طريقة إستخدام مكتبة جاهزة لإختيار التاريخ.
- طريقة البحث و الإنتقال السريع في الجدول.
- طريقة جعل التصميم مميزاً.
⇓ تحميل البرنامج بدون قاعدة البيانات ⇓ تحميل قاعدة البيانات ⇓ تحميل المشروع كاملاً ⇓ تحميل مجلد الصور فقط ⇓ تحميل المكتبات المستخدمة فقط
مميزات البرنامج
- هذا البرنامج يشبه كثيراً برامج إدارة المخزون التي يمكنك بيعها و الإستفادة منها.
- تم تجهيزه ليكون قابلاً للتعديل و التطوير بسهولة.
الخطوات التي يجب اتباعها لإنشاء المشروع
حتى لا تظهر عندك أي أخطاء أثناء نسخ الكود, يجب إتباع الخطوات التالية لإنشاء المشروع بنجاح:
- بناء قاعدة البيانات و التأكد من إسم المستخدم فيها و كلمة مروره لأنك بحاجة إليهم.
- إنشاء مشروع جديد لا يحتوي على أي كلاس.
- تضمين مكتبة الوقت و مكتبة قاعدة البيانات ( و التي تختلف على حسب نوع قاعدة البيانات المستخدمة ) في المشروع.
- تضمين مجلد الصور.
- إنشاء ملفات الجافا بالترتيب التالي:
DBInfo
, ثمProduct
, ثمAddNewProductDialog
, ثمMain
.
إذاً, يجب تجهيز قاعدة البيانات, المكتبات و الصور قبل المباشرة بكتابة كود الجافا.
ليس عندي خبرة في قواعد البيانات, ماذا أفعل؟
سواء قمت بتحميل البرنامج أو المشروع كاملاً فإنك لا زلت بحاجة إلى إنشاء قاعدة البيانات على جهازك.
إضافةً إلى ذلك لا تنسى أنه يجب تشغيل السيرفر و تفعيل خدمتي الإستضافة و الإتصال بقاعدة البيانات قبل تشغيل البرنامج.
لإنشاء قاعدة البيانات عندك خيارين:
- يمكنك تحميل ملف قاعدة البيانات products_db.sql, ثم إنشاء قاعدة بيانات إسمها
products_db
, بعدها تفعل import فقط للملف في قاعدة البياناتproducts_db
. - يمكنك إنشاء قاعدة البيانات من الصفر.
تعلم طريقة إنشاء قاعدة البيانات من الصفر
بناء البرنامج
بالنسبة للكود, قمنا بتقسيم هذا المشروع إلى إنترفيس واحد و ثلاث كلاسات.
- الإنترفيس
DBInfo
وضعنا فيه المعلومات الأساسية التي نحتاجها للوصول إلى قاعدة البيانات. - الكلاس
Product
يمثل المعلومات التي يمكن أن يحتويها كل منتج سيتم إضافته, هذا الأمر يسهل كثيراً طريقة جلب المعلومات المتوفرة حول أي منتج من قاعدة البيانات و وضعها في الجدول. - الكلاس
AddNewProductDialog
يمثل النافذة المنبثقة المخصصة لإضافة منتج جديد. - الكلاس
Main
يمثل النافذة الأساسية في البرنامج.
بعد إضافة جميع الملفات في المشروع, يجب أن تبدو كما في الصورة التالية.
كود البرنامج
// company_products_manager موجود بداخل المجلد DBInfo.java هنا ذكرنا أن الملف package company_products_manager; public interface DBInfo { // هنا قمنا بتحديد إسم قاعدة البيانات و طريقة الوصول لقاعدة البيانات String DB_NAME = "jdbc:mysql://localhost/products_db"; // هنا قمنا بتحديد نوع الترميز الذي سنستخدمه عند الإتصال بقاعدة البيانات. هذا الترميز يتيح لك تخزين البيانات باللغة العربية String ENCODING = "?useUnicode=yes&characterEncoding=UTF-8"; // هنا قمنا بدمج إسم قاعدة البيانات و طريقة الوصول إليها و نوع الترميز في متغير واحد بهدف تقليل حجم الكود لاحقاً فقط String DB_NAME_WITH_ENCODING = DB_NAME + ENCODING; // هنا قمنا بتحديد إسم المستخدم في قاعدة البيانات String USER = "root"; // هنا قمنا بتحديد كلمة مرور المستخدم في قاعدة البيانات String PASSWORD = ""; }
// company_products_manager موجود بداخل المجلد Product.java هنا ذكرنا أن الملف package company_products_manager; public class Product { // هنا قمنا بتحديد المعلومات الأساسية التي يمكن أن يحتويها كل منتج private final int id; private final String name; private final float price; private final String addDate; private final byte[] image; // هنا قلنا أنه سيتم تحديد خصائص المنتج لحظة إنشاء كائن من هذا الكلاس public Product(int id, String name, float price, String addDate, byte[] image) { this.id = id; this.name = name; this.price = price; this.addDate = addDate; this.image = image; } // هنا قمنا ببناء مجموعة دوال لإسترجاع المعلومات الأساسية الموجودة في الكائن, أي في المنتج public int getId() { return id; } public String getName() { return name; } public float getPrice() { return price; } public String getAddDate() { return addDate; } public byte[] getImage() { return image; } }
// company_products_manager موجود بداخل المجلد AddNewProductDialog.java هنا ذكرنا أن الملف package company_products_manager; import java.awt.Color; import java.awt.Font; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import javax.swing.BorderFactory; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JTextField; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableModel; // ليحصل على معلومات الإتصال بقاعدة البيانات DBInfo ينفذ الإنترفيس AddNewProductDialog هنا جعلنا الكلاس // لأننا سنحدد بداخله ماذا يحدث عند النقر على الأزرار ActionListener و جعلناه أيضأ ينفذ الإنترفيس public class AddNewProductDialog implements DBInfo, ActionListener { // هنا قمنا بتعريف النافذة المنبثقة و محتوياتها JDialog dialog; JLabel image, nameLabel, priceLabel; JTextField nameField, priceField; JButton chooseImageButton, addButton; String selectedImagePath = null; DefaultTableModel model; JFrame frame; // عند إنشاء كائن من هذا الكلاس, يجب تمرير النافذة التي سيظهر فيها و الجدول الذي سيتم إضافة المنتج الجديد فيه public AddNewProductDialog(JFrame frame, DefaultTableModel model) { // هنا قمنا بإنشاء النافذة المنبثقة و محتواها و ربطناها بنافذة البرنامج الأساسية و بجدول المنتجات dialog = new JDialog(frame); image = new JLabel("", JLabel.CENTER); chooseImageButton = new JButton("choose Image", new ImageIcon(this.getClass().getResource("/images/add-image.png"))); nameLabel = new JLabel("Name"); priceLabel = new JLabel("Price ( $ )"); nameField = new JTextField(); priceField = new JTextField(); addButton = new JButton("Add Product", new ImageIcon(this.getClass().getResource("/images/add-product.png"))); this.model = model; this.frame = frame; // هنا قمنا بتحديد حجم و مكان كل شيء سيتم إضافته في النافذة المنبثقة image.setBounds(36, 40, 224, 224); nameLabel.setBounds(300, 30, 80, 40); nameField.setBounds(300, 70, 270, 40); priceLabel.setBounds(300, 120, 80, 40); priceField.setBounds(300, 160, 270, 40); chooseImageButton.setBounds(298, 220, 274, 45); addButton.setBounds(34, 310, 538, 60); // هنا قمنا بتحديد خصائص الأشياء التي سيتم إضافتها في النافذة المنبثقة chooseImageButton.setFont(new Font("Arial", Font.BOLD, 15)); addButton.setForeground(Color.white); addButton.setBackground(Color.black); addButton.setFont(new Font("Arial", Font.BOLD, 18)); image.setBackground(Color.gray); image.setForeground(Color.white); image.setOpaque(true); image.setBorder(BorderFactory.createLineBorder(Color.gray, 1, true)); image.setFont(new Font("Arial", Font.BOLD, 22)); nameLabel.setFont(new Font("Arial", Font.BOLD, 16)); nameField.setFont(new Font("Arial", Font.BOLD, 15)); nameField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); priceLabel.setFont(new Font("Arial", Font.BOLD, 16)); priceField.setFont(new Font("Arial", Font.BOLD, 15)); priceField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); // هنا قمنا بإضافة جميع الأشياء التي قمنا بإنشائها في النافذة المنبثقة dialog.add(image); dialog.add(chooseImageButton); dialog.add(nameLabel); dialog.add(priceLabel); dialog.add(nameField); dialog.add(priceField); dialog.add(addButton); // عند النقر على أي زر موجود في النافذة المنبثقة actionPerformed() هنا قلنا أنه سيتم إستدعاء الدالة chooseImageButton.addActionListener(this); addButton.addActionListener(this); // هنا قمنا بتحديد خصائص و مكان ظهور النافذة المنبثقة dialog.setLayout(null); dialog.setSize(630, 420); dialog.setTitle("Add New Product"); dialog.setModal(false); dialog.setResizable(false); dialog.setLocationRelativeTo(frame); } // و التي سنستخدمها لإظهار النافذة المنبثقة show() هنا قمنا ببناء الدالة public void show() { nameField.setText(""); priceField.setText(""); image.setText("No image selected"); image.setIcon(null); dialog.setVisible(true); } // و التي سنستخدمها لإخفاء النافذة المنبثقة hide() هنا قمنا ببناء الدالة public void hide() { dialog.setVisible(false); } // و التي سنستخدمها كلما أردنا الإتصال بقاعدة البيانات getConnection() هنا قمنا ببناء الدالة private Connection getConnection() { Connection con; try { // DBInfo معلومات الإتصال بقاعدة البيانات قمنا بجلبها من الإنترفيس con = DriverManager.getConnection(DBInfo.DB_NAME_WITH_ENCODING, DBInfo.USER, DBInfo.PASSWORD); return con; } catch (SQLException ex) { JOptionPane.showMessageDialog(dialog, ex.getMessage(), "Connection Error", JOptionPane.ERROR_MESSAGE); return null; } } // و التي سنقوم باستدعائها كلما تم إضافة منتج جديد لإظهاره في الجدول viewProductsInTheTable() هنا قمنا ببناء الدالة private void viewProductsInTheTable() { // لتخزين معلومات منتج واحد في كل عنصر فيه productقمنا بإنشاء المصفوفة ArrayList<Product> product= new ArrayList(); // products هنا قمنا بالإتصال بقاعدة البيانات و بتجهيز الإستعلام الذي سيجلب جميع قيم الجدول Connection con = getConnection(); String query = "SELECT * FROM products"; // لتخزين نتيجة الإستعلام rs لتنفيذ الإستعلام, و الكائن st هنا قمنا بإنشاء الكائن Statement st; ResultSet rs; try { // rs هنا قمنا بتنفيذ الإستعلام و تخزين نتيجته في الكائن st = con.createStatement(); rs = st.executeQuery(query); // في كل مرة rs لتخزين منتج واحد من المنتجات التي ستكون موجودة في الكائن product هنا قمنا بإنشاء الكائن Product product; // الحلقة التالية ترجع سطر واحد في كل مرة, أي معلومات منتج واحد while(rs.next()) { // product بيانات المنتج التي سيتم إرجاعها في كل مرة سيتم تخزينها بشكل مؤقت في الكائن product = new Product( rs.getInt("id"), rs.getString("name"), Float.parseFloat(rs.getString("price")), rs.getString("add_date"), rs.getBytes("image") ); // productكعنصر واحد في المصفوفة product في الأخير سيتم إضافة الكائن productList.add(product); } // هنا قمنا بإغلاق الإتصال مع قاعدة البيانات لأننا لم نعد بحاجة إليها con.close(); } catch (SQLException ex) { JOptionPane.showMessageDialog(dialog, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } // هنا قمنا بمسح جميع أسطر الجدول model.setRowCount(0); // لتخزين سطر واحد في الجدول يتألف من 4 أعمدة في كل مرة row هنا قمنا بإنشاء المصفوفة Object[] row = new Object[4]; // row في المصفوفة productهنا في كل مرة سيتم تخزين معلومات منتج واحد من المنتجات المخزنة في المصفوفة for(int i = 0; i<productList.size(); i++) { row[0] = productList.get(i).getId(); row[1] = productList.get(i).getName(); row[2] = productList.get(i).getPrice(); row[3] = productList.get(i).getAddDate(); // كسطر واحد في الجدول row بعدها سيتم إضافة عناصر المصفوفة model.addRow(row); } } // لفحص القيم التي أدخلها المستخدم في الحقول للتأكد من صحتها قبل إضافة المنتج في قاعدة البيانات checkInputs() قمنا ببناء الدالة private boolean checkInputs() { if( nameField.getText().equals("") && priceField.getText().equals("") ) { JOptionPane.showMessageDialog(dialog, "Name and Price fields cannot be empty !", "", JOptionPane.PLAIN_MESSAGE); return false; } else if(nameField.getText().equals("")) { JOptionPane.showMessageDialog(dialog, "Please enter product name", "", JOptionPane.PLAIN_MESSAGE); return false; } else if(priceField.getText().equals("")) { JOptionPane.showMessageDialog(dialog, "Please enter product price", "", JOptionPane.PLAIN_MESSAGE); return false; } try { Float.parseFloat(priceField.getText()); return true; } catch(NumberFormatException ex) { JOptionPane.showMessageDialog(dialog, "<html>" + "<b>Price should be a decimal number.<br><br>" + "Examples:<br>" + "• 40<br>" + "• 10.5</b>" + "</html>", "Error", JOptionPane.ERROR_MESSAGE); return false; } } // لتعديل حجم أي صورة يختارها المستخدم للمنتج resizeImage() قمنا ببناء الدالة // لجعله يساوي حجم المكان المخصص لعرض الصورة التي تم إختيارها // تمثل الصورة التي يجب تعديل حجمها bytes عند إستدعائها يجب تمرير مصفوفة من الـ // no-image.jpg في حال لم يتم تمرير مصفوفة لها, سترجع صورة معدلة الحجم من الصورة الإفتراضية private ImageIcon resizeImage(byte[] pic) { // لحفظ الصورة التي سيتم تعديل حجمها myImage قمنا بتعريف الكائن ImageIcon myImage; // myImage في الكائن no-image.jpg سيتم تخزين الصورة pic في حال لم يتم تمرير مصفوفة تمثل صورة مكان البارامتير if(pic == null) myImage = new ImageIcon(this.getClass().getResource("/images/no-image.jpg")); // myImage تمثل صورة, سيتم تخزين هذه المصفوفة في الكائن bytes في حال تم تمرير مصفوفة من الـ else myImage = new ImageIcon(pic); // tempImage ثم قمنا بتخزينها بشكل مؤقت في الكائن myImage هنا قمنا بإنشاء صورة معدلة الحجم من الصورة المخزنة في الكائن Image tempImage = myImage.getImage().getScaledInstance(image.getWidth(), image.getHeight(), Image.SCALE_SMOOTH); // tempImage في الأخير قمنا بإرجاع الصورة المعدلة الحجم في الكائن return new ImageIcon(tempImage); } // لجعل المستخدم قادر على إختيار صورة موجودة في حاسوبه chooseImage() قمنا ببناء الدالة // Choose Image سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر private void chooseImage() { // و الذي سيمثل نافذة منبثقة لإختيار صورة من الجهاز JFileChooser هنا قمنا بإنشاء كائن من الكلاس JFileChooser file = new JFileChooser(); file.setCurrentDirectory(new File(System.getProperty("user.home"))); // هنا قمنا بتحديد نوع الصور التي يمكنك للمستخدم إختيارها من جهازه FileNameExtensionFilter filter = new FileNameExtensionFilter("Select a .JPG .PNG .GIF image", "jpg", "png", "gif"); file.setFileFilter(filter); // هنا قمنا بإظهار النافذة و تخزين قيمة الزر الذي تم النقر عليه و أدى إلى إغلاق النافذة int result = file.showOpenDialog(dialog); // سيتم تعديل حجمها ليوافق حجم المربع, ثم ستوضع فيه Open بعد إختيار الصورة, إذا قام المستخدم بالنقر على الزر if(result == JFileChooser.APPROVE_OPTION) { try { byte[] selectedImage = Files.readAllBytes(file.getSelectedFile().toPath()); image.setIcon(resizeImage(selectedImage)); image.setText(""); selectedImagePath = file.getSelectedFile().toPath().toString(); } catch(IOException ex) { image.setIcon(resizeImage(null)); } } } // لحفظ جميع المعلومات التي أدخلها المستخدم في الحقول إضافةً إلى الصورة التي إختارها في قاعدة البيانات insertProduct() قمنا ببناء الدالة private void insertProduct() { // إذا تم التشييك على الحقول و كان لا يوجد أي خطأ أو نقص في المعلومات المطلوب إدخالها if(checkInputs()) { try { // سيتم الإتصال مع قاعدة البيانات Connection con = getConnection(); // ثم سيتم تجهيز الإستعلام الذي سيتم تنفيذه في ثاعدة البيانات لحفظ المعلومات المدخلة في الحقول PreparedStatement ps; // إذا لم يقم المستخدم بوضع صورة للمنتج, سيتم تخزين فقط المعلومات التي أدخلها في الحقول if (selectedImagePath == null) { ps = con.prepareStatement("INSERT INTO products(name, price, add_date) values(?,?,?)"); } // إذا قام المستخدم بوضع صورة للمنتج, سيتم تخزين المعلومات التي أدخلها في الحقول و الصورة التي إختارها أيضاً else { ps = con.prepareStatement("INSERT INTO products(name, price, add_date, image) values(?,?,?,?)"); InputStream img = new FileInputStream(new File(selectedImagePath)); ps.setBlob(4, img); } ps.setString(1, nameField.getText()); ps.setString(2, priceField.getText()); // تاريخ إضافة المنتج سيتم تخزينه بشكل تلقائي SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); String addDate = dateFormat.format(new Date()); ps.setString(3, addDate); // في الأخير سيتم تنفيذ الإستعلام و إغلاق الإتصال مع قاعدة البيانات ps.executeUpdate(); con.close(); // بعدها سيتم إظهار المنتج الذي تم إضتفته في الجدول viewProductsInTheTable(); // ثم سيتم مسح جميع المعلومات التي أدخلها المستخدم في الحقول, حتى يتمكن من إدخال معلومات منتج جديد بسرعة nameField.setText(""); priceField.setText(""); image.setText("No image selected"); image.setIcon(null); selectedImagePath = null; } catch (FileNotFoundException | SQLException ex) { JOptionPane.showMessageDialog(dialog, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } // هذه الدالة يتم إستدعاءها إذا تم النقر على أي زر موجود في النافذة المنبثقة public void actionPerformed(ActionEvent e) { // لفتح نافذة منبثقة تسمح بإختيار صورة للمنتج chooseImage() سيتم إستدعاء الدالة chooseImageButton إذا تم النقر على الزر if( e.getSource() == chooseImageButton) chooseImage(); // لإضافة المنتج في قاعدة البيانات insertProduct() سيتم إستدعاء الدالة addButton إذا تم النقر على الزر else if(e.getSource() == addButton) insertProduct(); } }
// company_products_manager موجود بداخل المجلد Main.java هنا ذكرنا أن الملف package company_products_manager; import java.awt.Color; import java.awt.Dimension; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingUtilities; import com.toedter.calendar.JDateChooser; import java.awt.Font; import java.awt.GridBagLayout; import java.awt.HeadlessException; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import javax.swing.BorderFactory; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.RowFilter; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableRowSorter; // و بالتالي أصبح إنشاء كائن منه يمثل إنشاء نافذة JFrame يرث من الكلاس Main هنا جعلنا الكلاس public class Main extends JFrame implements DBInfo, ActionListener { // هنا قمنا بتعريف جميع الأشياء التي سنضيفها في النافذة JPanel panel; JLabel image, idLabel, nameLabel, priceLabel, dateLabel, searchLabel, moveFastLabel; JTextField idField, nameField, priceField, searchField; JDateChooser dateField; JButton updateImageButton, insertButton, updateButton, deleteButton ,exitButton, selectFirstButton, selectNextButton, selectPreviousButton, selectLastButton; JTable table; JScrollPane tableScroller; DefaultTableModel model; String currentImagePath = null; AddNewProductDialog addProductDialog; // فقط createAndShowGUI() سيقوم الكونستركتور بإستدعاء الدالة Main عند إنشاء كائن من الكلاس public Main() { createAndShowGUI(); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { // التي ستنشئ النافذة createAndShowGUI() و بالتالي سيتم إستدعاء الدالة Main هنا قمنا بإنشاء كائن من الكلاس new Main(); } }); } // هنا نضع كود إنشاء النافذة و محتوياتها private void createAndShowGUI() { // لجعل محتوى االنافذة يظهر بشكل أجمل Nimbus إلى الـ UIManager هنا قمنا بتغير الـ try { for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { if ("Nimbus".equals(info.getName())) { UIManager.setLookAndFeel(info.getClassName()); break; } } } catch (ClassNotFoundException | IllegalAccessException | InstantiationException | UnsupportedLookAndFeelException ex) { } // هنا قمنا بإنشاء جميع الأشياء التي سنضيفها في النافذة panel = new JPanel(null); model = new DefaultTableModel(); table = new JTable(model); tableScroller = new JScrollPane(table); image = new JLabel(); idLabel = new JLabel("ID"); nameLabel = new JLabel("Name"); priceLabel = new JLabel("Price"); dateLabel = new JLabel("Date"); searchLabel = new JLabel(); moveFastLabel = new JLabel("Move Fast", JLabel.CENTER); idField = new JTextField(); nameField = new JTextField(); priceField = new JTextField(); dateField = new JDateChooser(); searchField = new JTextField(); updateImageButton = new JButton("Update Image"); insertButton = new JButton("Add New", new ImageIcon(this.getClass().getResource("/images/insert.png"))); updateButton = new JButton("Update", new ImageIcon(this.getClass().getResource("/images/update.png"))); deleteButton = new JButton("Delete", new ImageIcon(this.getClass().getResource("/images/delete.png"))); searchLabel = new JLabel("Search"); selectFirstButton = new JButton("First", new ImageIcon(this.getClass().getResource("/images/first.png"))); selectLastButton = new JButton("Last", new ImageIcon(this.getClass().getResource("/images/last.png"))); selectNextButton = new JButton("Next", new ImageIcon(this.getClass().getResource("/images/next.png"))); selectPreviousButton = new JButton("Previous", new ImageIcon(this.getClass().getResource("/images/previous.png"))); exitButton = new JButton("Exit", new ImageIcon(this.getClass().getResource("/images/exit.png"))); addProductDialog = new AddNewProductDialog(this, model); // هنا قمنا بتحديد حجم و مكان كل شيء سيتم إضافته في النافذة image.setBounds(80, 41, 270, 250); updateImageButton.setBounds(150, 300, 130, 34); idLabel.setBounds(20, 355, 50, 40); idField.setBounds(80, 355, 270, 40); nameLabel.setBounds(20, 405, 50, 40); nameField.setBounds(80, 405, 270, 40); priceLabel.setBounds(20, 455, 50, 40); priceField.setBounds(80, 455, 270, 40); dateLabel.setBounds(20, 505, 50, 40); dateField.setBounds(80, 505, 270, 40); deleteButton.setBounds(80, 575, 130, 40); updateButton.setBounds(220, 575, 130, 40); tableScroller.setBounds(377, 40, 520, 505); searchField.setBounds(530, 577, 255, 36); searchLabel.setBounds(460, 575, 115, 40); insertButton.setBounds(920, 40, 130, 60); moveFastLabel.setBounds(890, 150, 190, 30); selectFirstButton.setBounds(920, 200, 130, 40); selectLastButton.setBounds(920, 250, 130, 40); selectNextButton.setBounds(920, 300, 130, 40); selectPreviousButton.setBounds(920, 350, 130, 40); exitButton.setBounds(920, 575, 130, 40); // هنا قمنا بتحديد نوع و حجم خط الأشياء التي سيتم إضافتها في النافذة updateImageButton.setFont(new Font("Arial", Font.BOLD, 14)); idLabel.setFont(new Font("Arial", Font.BOLD, 16)); idField.setFont(new Font("Arial", Font.BOLD, 15)); nameLabel.setFont(new Font("Arial", Font.BOLD, 16)); nameField.setFont(new Font("Arial", Font.BOLD, 15)); priceLabel.setFont(new Font("Arial", Font.BOLD, 16)); priceField.setFont(new Font("Arial", Font.BOLD, 15)); dateLabel.setFont(new Font("Arial", Font.BOLD, 16)); dateField.setFont(new Font("Arial", Font.BOLD, 13)); deleteButton.setFont(new Font("Arial", Font.BOLD, 16)); updateButton.setFont(new Font("Arial", Font.BOLD, 16)); insertButton.setFont(new Font("Arial", Font.BOLD, 16)); searchField.setFont(new Font("Arial", Font.BOLD, 15)); searchLabel.setFont(new Font("Arial", Font.BOLD, 17)); moveFastLabel.setFont(new Font("Arial", Font.BOLD, 22)); selectFirstButton.setFont(new Font("Arial", Font.BOLD, 16)); selectLastButton.setFont(new Font("Arial", Font.BOLD, 16)); selectNextButton.setFont(new Font("Arial", Font.BOLD, 16)); selectPreviousButton.setFont(new Font("Arial", Font.BOLD, 16)); exitButton.setFont(new Font("Arial", Font.BOLD, 16)); // هنا قمنا بإضافة خط أسود حول الصورة و الحقول لإظهار التصميم بشكل أجمل image.setBorder(BorderFactory.createLineBorder(Color.gray, 1, true)); idField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); nameField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); priceField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); searchField.setBorder(BorderFactory.createLineBorder(Color.gray, 2, true)); // غير قابل للتعديل و جعلنا لون الخلفية رمادي idField هنا جعلنا الرقم الظاهر في الحقل idField.setEditable(false); idField.setBackground(new Color(240, 240, 240)); // و إظهار تصميمه بشكل أجمل dateField هنا قمنا بتحديد طريقة ظهور التاريخ في الحقل dateField.setDateFormatString("yyyy-MM-dd"); dateField.setBackground(Color.gray); dateField.getCalendarButton().setIcon(new ImageIcon(this.getClass().getResource("/images/calendar.png"))); dateField.getCalendarButton().setBackground(Color.gray); // و جعلنا لون خلفيته أبيض table هنا قمنا بتعديل بعض خصائص الجدول table.setColumnSelectionAllowed(false); table.getParent().setBackground(Color.white); tableScroller.setViewportView(table); // table هنا قمنا بتحديد أسماء الأعمدة في الجدول model.addColumn("ID"); model.addColumn("Name"); model.addColumn("Price ($)"); model.addColumn("Date Of Add"); // عند تشغيل البرنامج سيتم إظهار جميع المنتجات المخزنة سابقاً و إظهار معلومات أول منتج فيه try { viewProductsInTheTable(); } catch(Exception e) { } // هنا قلنا أنه عند النقر على أي منتج في الجدول بواسطة الفأرة سيتم عرض جميع معلوماته table.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent evt) { int index = table.getSelectedRow(); showProduct(index); currentImagePath = null; } }); // هنا قلنا أنه عند تحديد منتج في الجدول بواسطة لوحة المفاتيح سيتم عرض جميع معلوماته table.addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { if(e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_DOWN ) showProduct(table.getSelectedRow()); } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { } }); // لإظهار المنتجات التي تطابقت مع البحث فقط search() هنا قلنا أنه عند إدخال أي نص في مربع البحث سيتم إستدعاء الدالة searchField.addKeyListener(new KeyListener() { public void keyReleased(KeyEvent e) { search(); } public void keyTyped(KeyEvent e) { } public void keyPressed(KeyEvent e) { } }); // actionPerformed() هنا قلنا أنه عند النقر على أي زر في النافذة سيتم إستدعاء الدالة updateImageButton.addActionListener(this); insertButton.addActionListener(this); updateButton.addActionListener(this); deleteButton.addActionListener(this); selectFirstButton.addActionListener(this); selectLastButton.addActionListener(this); selectNextButton.addActionListener(this); selectPreviousButton.addActionListener(this); exitButton.addActionListener(this); // panel هنا قمنا بإضافة جميع الأشياء التي قما بتعريفها في الحاوية panel.add(image); panel.add(updateImageButton); panel.add(idLabel); panel.add(idField); panel.add(idField); panel.add(nameLabel); panel.add(nameField); panel.add(priceLabel); panel.add(priceField); panel.add(dateLabel); panel.add(dateField); panel.add(insertButton); panel.add(updateButton); panel.add(deleteButton); panel.add(tableScroller); panel.add(searchField); panel.add(searchLabel); panel.add(moveFastLabel); panel.add(selectFirstButton); panel.add(selectLastButton); panel.add(selectNextButton); panel.add(selectPreviousButton); panel.add(exitButton); // هنا قمنا بإظهار معلومات أول منتج في الجدول showFirstProduct(); // المفضل و في حال تم تصيغر النافذة panel هنا قمنا بتحديد حجم الحاوية panel.setPreferredSize(new Dimension(1070, 640)); panel.setMinimumSize(new Dimension(1070, 640)); // لترتيب الأشياء التي ستوضع فيها. و جعلناها تمثل الحاوية الأساسية في النافذة GridBagLayout هنا قمنا بإنشاء حاوية جديدة تستخدم الـ setContentPane(new JPanel(new GridBagLayout())); // التي تحتوي على كل ما أضفناه في النافذة في حاوية النافذة الأساسية و هكذا سيظل محتوى النافذة يظهر في الوسط panel هنا قمنا بإضافة الحاوية add(panel); // هنا قمنا بتحديد بعض خصائص النافذة و جعلناها مرئية setTitle("Company Products Manager"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); pack(); setLocationRelativeTo(null); setVisible(true); } // و التي سنستخدمها كلما أردنا الإتصال بقاعدة البيانات getConnection() هنا قمنا ببناء الدالة private Connection getConnection() { Connection con; try { // DBInfo معلومات الإتصال بقاعدة البيانات قمنا بجلبها من الإنترفيس con = DriverManager.getConnection(DBInfo.DB_NAME_WITH_ENCODING, DBInfo.USER, DBInfo.PASSWORD); return con; } catch (SQLException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Connection Error", JOptionPane.ERROR_MESSAGE); return null; } } // و التي سنقوم باستدعائها كلما تم تحديث أو حذف منتج في الجدول لإظهار جميع المنتجات المتوفرة viewProductsInTheTable() هنا قمنا ببناء الدالة private void viewProductsInTheTable() { // لتخزين معلومات منتج واحد في كل عنصر فيه productقمنا بإنشاء المصفوفة ArrayList<Product> product= new ArrayList(); // products هنا قمنا بالإتصال بقاعدة البيانات و بتجهيز الإستعلام الذي سيجلب جميع قيم الجدول Connection con = getConnection(); String query = "SELECT * FROM products"; // لتخزين نتيجة الإستعلام rs لتنفيذ الإستعلام, و الكائن st هنا قمنا بإنشاء الكائن Statement st; ResultSet rs; try { // rs هنا قمنا بتنفيذ الإستعلام و تخزين نتيجته في الكائن st = con.createStatement(); rs = st.executeQuery(query); // في كل مرة rs لتخزين منتج واحد من المنتجات التي ستكون موجودة في الكائن product هنا قمنا بإنشاء الكائن Product product; // الحلقة التالية ترجع سطر واحد في كل مرة, أي معلومات منتج واحد while(rs.next()) { // product بيانات المنتج التي سيتم إرجاعها في كل مرة سيتم تخزينها بشكل مؤقت في الكائن product = new Product( rs.getInt("id"), rs.getString("name"), Float.parseFloat(rs.getString("price")), rs.getString("add_date"), rs.getBytes("image") ); // productكعنصر واحد في المصفوفة product في الأخير سيتم إضافة الكائن productList.add(product); } // هنا قمنا بإغلاق الإتصال مع قاعدة البيانات لأننا لم نعد بحاجة إليها con.close(); } catch (SQLException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } // هنا قمنا بمسح جميع أسطر الجدول model.setRowCount(0); // لتخزين سطر واحد في الجدول يتألف من 4 أعمدة في كل مرة row هنا قمنا بإنشاء المصفوفة Object[] row = new Object[4]; // row في المصفوفة productهنا في كل مرة سيتم تخزين معلومات منتج واحد من المنتجات المخزنة في المصفوفة for(int i = 0; i<productList.size(); i++) { row[0] = productList.get(i).getId(); row[1] = productList.get(i).getName(); row[2] = productList.get(i).getPrice(); row[3] = productList.get(i).getAddDate(); // كسطر واحد في الجدول row بعدها سيتم إضافة عناصر المصفوفة model.addRow(row); } } // لفحص القيم التي أدخلها المستخدم في الحقول للتأكد من صحتها قبل إضافة المنتج في قاعدة البيانات checkInputs() قمنا ببناء الدالة private boolean checkInputs() { if( nameField.getText().equals("") || priceField.getText().equals("") || dateField.getDate() == null ) { JOptionPane.showMessageDialog(this, "Product information are not updated because one or more fields are empty", "Error", JOptionPane.ERROR_MESSAGE); return false; } else { try { Float.parseFloat(priceField.getText()); return true; } catch(NumberFormatException ex) { return false; } } } // لتعديل حجم أي صورة يختارها المستخدم للمنتج resizeImage() قمنا ببناء الدالة // لجعله يساوي حجم المكان المخصص لعرض الصورة التي تم إختيارها // تمثل الصورة التي يجب تعديل حجمها bytes عند إستدعائها يجب تمرير مصفوفة من الـ // no-image.jpg في حال لم يتم تمرير مصفوفة لها, سترجع صورة معدلة الحجم من الصورة الإفتراضية private ImageIcon resizeImage(byte[] pic) { // لحفظ الصورة التي سيتم تعديل حجمها myImage قمنا بتعريف الكائن ImageIcon myImage; // myImage في الكائن no-image.jpg سيتم تخزين الصورة pic في حال لم يتم تمرير مصفوفة تمثل صورة مكان البارامتير if(pic == null) myImage = new ImageIcon(this.getClass().getResource("/images/no-image.jpg")); // myImage تمثل صورة, سيتم تخزين هذه المصفوفة في الكائن bytes في حال تم تمرير مصفوفة من الـ else myImage = new ImageIcon(pic); // tempImage ثم قمنا بتخزينها بشكل مؤقت في الكائن myImage هنا قمنا بإنشاء صورة معدلة الحجم من الصورة المخزنة في الكائن Image tempImage = myImage.getImage().getScaledInstance(image.getWidth(), image.getHeight(), Image.SCALE_SMOOTH); // tempImage في الأخير قمنا بإرجاع الصورة المعدلة الحجم في الكائن return new ImageIcon(tempImage); } // لجعل المستخدم قادر على تحديث صورة المنتج من حاسوبه updateImage() قمنا ببناء الدالة // updateImage سيتم إستدعاء هذه الدالة عندما يقوم المستخدم بالنقر على الزر private void updateImage() { // و الذي سيمثل نافذة منبثقة لإختيار صورة من الجهاز JFileChooser هنا قمنا بإنشاء كائن من الكلاس JFileChooser file = new JFileChooser(); file.setCurrentDirectory(new File(System.getProperty("user.home"))); // هنا قمنا بتحديد نوع الصور التي يمكنك للمستخدم إختيارها من جهازه FileNameExtensionFilter filter = new FileNameExtensionFilter("Select a .JPG .PNG .GIF image", "jpg", "png", "gif"); file.setFileFilter(filter); // هنا قمنا بإظهار النافذة و تخزين قيمة الزر الذي تم النقر عليه و أدى إلى إغلاق النافذة int result = file.showOpenDialog(this); // سيتم تعديل حجمها ليوافق حجم المربع, ثم ستوضع فيه Open بعد إختيار الصورة, إذا قام المستخدم بالنقر على الزر // update لإرسالها لاحقاَ عند النقر على زر التحديث currentImagePath و سيتم حفظ مسار الصورة التي تم اختيارها في المتغير if(result == JFileChooser.APPROVE_OPTION) { try{ byte[] selectedImage = Files.readAllBytes(file.getSelectedFile().toPath()); image.setIcon(resizeImage(selectedImage)); currentImagePath = file.getSelectedFile().toPath().toString(); } catch(IOException ex) { image.setIcon(resizeImage(null)); } } } // لإظهار النافذة التي تسمح بإدخال منتج جديد عندما يتم إستدعاءها addNewProduct() قمنا ببناء الدالة private void addNewProduct() { addProductDialog.show(); } // قمنا ببناء هذه الدالة لتحديث معلومات المنتج private void updateProduct() { // إذا تم التشييك على الحقول و كان لا يوجد أي خطأ أو نقص في المعلومات المطلوب إدخالها // سيتم تحديث معلومات المنتج في قاعدة البيانات و في الجدول أيضاً if(checkInputs() && idField.getText() != null) { if(currentImagePath != null) { try { InputStream img = new FileInputStream(new File(currentImagePath)); String query = "UPDATE products SET name = ?, price = ?, add_date = ?, image = ? WHERE id = ?"; Connection con = getConnection(); PreparedStatement ps = con.prepareStatement(query); ps.setString(1, nameField.getText()); ps.setString(2, priceField.getText()); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); String addDate = dateFormat.format(dateField.getDate()); ps.setString(3, addDate); ps.setBlob(4, img); ps.setInt(5, Integer.parseInt(idField.getText())); ps.executeUpdate(); con.close(); viewProductsInTheTable(); JOptionPane.showMessageDialog(this, "Product information has been successfully updated"); } catch(HeadlessException | FileNotFoundException | NumberFormatException | SQLException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } else { try { String query = "UPDATE products SET name = ?, price = ?, add_date = ? WHERE id = ?"; Connection con = getConnection(); PreparedStatement ps = con.prepareStatement(query); ps.setString(1, nameField.getText()); ps.setString(2, priceField.getText()); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); String addDate = dateFormat.format(dateField.getDate()); ps.setString(3, addDate); ps.setInt(4, Integer.parseInt(idField.getText())); ps.executeUpdate(); con.close(); viewProductsInTheTable(); JOptionPane.showMessageDialog(this, "Product information are successfuly updated"); } catch(HeadlessException | NumberFormatException | SQLException ex){ JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } } // لتخزين جميع المعلومات المتوفرة حول المنتجات في قاعدة البيانات getProductList() قمنا ببناء الدالة private ArrayList<Product> getProductList() { ArrayList<Product> product= new ArrayList(); Connection con = getConnection(); String query = "SELECT * FROM products"; Statement st; ResultSet rs; try { st = con.createStatement(); rs = st.executeQuery(query); Product product; while(rs.next()) { product = new Product( rs.getInt("id"), rs.getString("name"), Float.parseFloat(rs.getString("price")), rs.getString("add_date"), rs.getBytes("image") ); productList.add(product); } con.close(); } catch (SQLException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } return productList; } // و التي سنقوم باستدعائها كلما تم تحديد منتج جديد في الجدول showProduct() هنا قمنا ببناء الدالة // الخاص بالمنتج id لعرض جميع المعلومات المتوفرة عنه. لاحظ أنه سيتم جلب جميع المعلومات من قاعدة البيانات بناءاً رقم private void showProduct(int index) { idField.setText(Integer.toString(getProductList().get(index).getId())); nameField.setText(getProductList().get(index).getName()); priceField.setText(Float.toString(getProductList().get(index).getPrice())); try { Date addDate = new SimpleDateFormat("yyyy-MM-dd").parse((String)getProductList().get(index).getAddDate()); dateField.setDate(addDate); } catch (ParseException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } byte[] theImage = getProductList().get(index).getImage(); image.setIcon(resizeImage(theImage)); } // و التي سنقوم باستدعائها كلما تم تحديد منتج جديد في الجدول بهدف مسحه نهائياً deleteProduct() هنا قمنا ببناء الدالة // مختلف id عند إجراء عملية الحذف, لأن كل منتج يملك رقم id ملاحظة: سيتم الإعتماد على رقم الـ // بعد أن تتم عملية المسح بنجاح, سيتم تحديد أقرب منتج كان قريب للمنتج الذي تم حذفه, بهدف عرض معلوماته // في حال تم مسح جميع المنتجات و لم يعد هناك أي عنصر آخر, عندها لن يتم عرض معلومات أي منتج, و لا حتى آخر منتج تم حذفه private void deleteProduct() { if(table.getSelectedRow() == -1) { JOptionPane.showMessageDialog(this, "Please select the product that you want to delete from the table and try again"); } else { try { Connection con = getConnection(); PreparedStatement ps = con.prepareStatement("DELETE FROM products WHERE id = ?"); int id = Integer.parseInt(idField.getText()); ps.setInt(1, id); ps.executeUpdate(); con.close(); int nextSelectedRowIndex = table.getSelectedRow(); viewProductsInTheTable(); if(table.getRowCount() == 1) { table.setRowSelectionInterval(0, 0); showProduct(0); } else if(table.getRowCount() > 1 && nextSelectedRowIndex < table.getRowCount()) { table.setRowSelectionInterval(nextSelectedRowIndex, nextSelectedRowIndex); showProduct(nextSelectedRowIndex); } else if(table.getRowCount() > 1 && nextSelectedRowIndex == table.getRowCount()) { nextSelectedRowIndex--; table.setRowSelectionInterval(nextSelectedRowIndex, nextSelectedRowIndex); showProduct(nextSelectedRowIndex); } if(table.getRowCount() == 0) { image.setIcon(null); idField.setText(""); nameField.setText(""); priceField.setText(""); dateField.setDate(null); } } catch (SQLException ex) { JOptionPane.showMessageDialog(this, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE); } } } // لتحديد أول منتج في الجدول ثم عرض معلوماته showFirstProduct() هنا قمنا ببناء الدالة private void showFirstProduct() { if(table.getRowCount() != 0) { table.setRowSelectionInterval(0, 0); showProduct(0); } } // لتحديد آخر منتج في الجدول ثم عرض معلوماته showlastProduct() هنا قمنا ببناء الدالة private void showLastProduct() { if(table.getRowCount() != 0) { table.setRowSelectionInterval(table.getRowCount()-1, table.getRowCount()-1); showProduct(table.getRowCount()-1); } } // لتحديد المنتج التالي في الجدول ثم عرض معلوماته showNextProduct() هنا قمنا ببناء الدالة private void showNextProdut() { if(table.getSelectedRow() < table.getRowCount()-1) { int currentSelectedRow = table.getSelectedRow()+1; table.setRowSelectionInterval(currentSelectedRow, currentSelectedRow); showProduct(currentSelectedRow); } } // لتحديد المنتج السابق في الجدول ثم عرض معلوماته showPreviousProduct() هنا قمنا ببناء الدالة private void showPreviousProduct() { if(table.getSelectedRow() > 0) { int currentSelectedRow = table.getSelectedRow()-1; table.setRowSelectionInterval(currentSelectedRow, currentSelectedRow); showProduct(currentSelectedRow); } } // لإظهار المنتجات التي تتطابق مع جملة البحث search() هنا قمنا ببناء الدالة private void search() { String keyword = searchField.getText(); TableRowSorter<DefaultTableModel> tr = new TableRowSorter<>(model); table.setRowSorter(tr); tr.setRowFilter(RowFilter.regexFilter(keyword)); } // هذه الدالة يتم إستدعاءها إذا تم النقر على أي زر موجود في النافذة public void actionPerformed(ActionEvent e) { // و التي ستظهر نافذة منبثقة لإختيار صورة جديدة للمنتج updateImage() سيتم إستدعاء الدالة updateImageButton إذا تم النقر على الزر if (e.getSource() == updateImageButton){ updateImage(); } // و التي ستظهر نافذة منبثقة لإضافة منتج addNewProduct() سيتم إستدعاء الدالة addNewProduct إذا تم النقر على الزر else if (e.getSource() == insertButton){ addNewProduct(); } // و التي ستقوم بتحديث بيانات المنتج updateProduct() سيتم إستدعاء الدالة updateButton إذا تم النقر على الزر else if (e.getSource() == updateButton){ updateProduct(); } // و التي ستقوم بحذف المنتج نهائياً deleteProduct() سيتم إستدعاء الدالة deleteButton إذا تم النقر على الزر else if (e.getSource() == deleteButton){ deleteProduct(); } // و التي ستقوم بعرض معلومات أول منتج في الجدول showFirstProduct() سيتم إستدعاء الدالة selectFirstProduct إذا تم النقر على الزر else if (e.getSource() == selectFirstButton){ showFirstProduct(); } // و التي ستقوم بعرض معلومات آخر منتج في الجدول showLastProduct() سيتم إستدعاء الدالة selectLastProduct إذا تم النقر على الزر else if (e.getSource() == selectLastButton){ showLastProduct(); } // و التي ستقوم بعرض معلومات المنتج التالي في الجدول showNextProduct() سيتم إستدعاء الدالة selectNextButton إذا تم النقر على الزر else if (e.getSource() == selectNextButton){ showNextProdut(); } // و التي ستقوم بعرض معلومات المنتج التالي في الجدول showPreviousProduct() سيتم إستدعاء الدالة selectPreviousButton إذا تم النقر على الزر else if (e.getSource() == selectPreviousButton){ showPreviousProduct(); } // سيتم إغلاق البرنامج exitButton إذا تم النقر على الزر else if (e.getSource() == exitButton){ System.exit(0); } } }