turns-00032.parquet:25205
1a1827eb4628a85b1daf1858
turn 1/1o1-preview-2024-09-12TurkishTürkiye12491 words
degenerate_repetitionAbsentFinal dense release
USER
Arayüz donma yapıyor, bunu çöz, datagridview için virtualmode kullan, uygulamanın akıcılığını artır. şimdi : Vereceğim tüm kodu fonksiyonelliği kesinlikle ! bozmadan, eksiksiz ilgili değişikliklerle birlikte tek parça yaz. using System;
using System.Windows.Forms;
using Firebase.Database;
using Firebase.Database.Query;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using System.Threading.Tasks;
using System.Linq;
using System.Drawing;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Firebase.Database.Streaming;
namespace Flashcard
{
public partial class MainForm : Form
{
// Firebase client
private FirebaseClient firebaseClient;
// UI Controls
private DataGridView dataGridView;
private TextBox txtTitle, txtCategory, txtContent, txtBackgroundColor, txtSearch;
private CheckBox chkFavorite, chkPinned;
private CheckBox chkFilterFavorites, chkFilterPinned; // Filtering CheckBoxes
private Button btnAdd, btnUpdate, btnDelete, btnSearch, btnClear;
private StatusStrip statusStrip;
private ToolStripStatusLabel statusLabel;
// Menü Kontrolleri
private MenuStrip menuStrip;
private ToolStripMenuItem fileMenuItem, themeMenuItem;
private ToolStripMenuItem saveAsMenuItem, lightModeMenuItem, darkModeMenuItem;
private ToolStripMenuItem clipboardModeMenuItem, clipboardOnMenuItem, clipboardOffMenuItem;
private ToolStripMenuItem loadTagsMenuItem;
// Custom Tag Menu Items
private ToolStripMenuItem customTagMenuItem;
private ToolStripMenuItem addTagMenuItem;
private ToolStripMenuItem manageTagsMenuItem;
private List<string> customTags = new List<string>();
private string selectedCustomTag = null;
// customTags'i kaydetmek için dosya yolu
private readonly string customTagsFilePath = "custom_tags.json";
// ContextMenuStrip for column visibility
private ContextMenuStrip columnContextMenu;
// Sorting state
private string currentSortColumn = "timestamp";
private bool sortAscending = false;
// Data storage
private List<FlashcardItem> allFlashcards = new List<FlashcardItem>();
// Current search term
private string currentSearchTerm = "";
// Dictionary to store column visibility state
private Dictionary<string, bool> columnVisibilityState = new Dictionary<string, bool>();
// Clipboard monitoring
private Timer clipboardMonitorTimer;
private string lastClipboardText;
// Yeni Eklenen Değişkenler
private CheckedListBox tagCheckedListBox;
private string selectedTag = "uncategorized";
// Local storage file path
private readonly string localDataFilePath = "local_flashcards.json";
// Flag to indicate if application is online
private bool isOnline = false;
// Flag to prevent multiple listeners
private bool isListening = false;
// Offline changes list
private List<FlashcardChange> offlineChanges = new List<FlashcardChange>();
// Timer to check online status
private Timer onlineStatusTimer;
public MainForm()
{
InitializeComponent();
InitializeFirebase();
InitializeUI();
}
/// <summary>
/// Initializes Firebase connection using the provided JSON credentials.
/// </summary>
private void InitializeFirebase()
{
var path = "flashcard-e7f47-firebase-adminsdk-om656-e70a5506c2.json";
var credential = GoogleCredential.FromFile(path);
// Check if FirebaseApp is already created to prevent duplicate initialization
if (FirebaseApp.DefaultInstance == null)
{
FirebaseApp.Create(new AppOptions
{
Credential = credential
});
}
firebaseClient = new FirebaseClient("https://flashcard-e7f47-default-rtdb.europe-west1.firebasedatabase.app/");
}
/// <summary>
/// Sets up the user interface components.
/// </summary>
private void InitializeUI()
{
this.Size = new Size(1000, 800);
this.Text = "Flashcard Application";
this.StartPosition = FormStartPosition.CenterScreen; // MainForm ekranın ortasında açılsın
// MenuStrip Oluşturma
menuStrip = new MenuStrip
{
Dock = DockStyle.Top // MenuStrip'i üstte konumlandır
};
// File Menu
fileMenuItem = new ToolStripMenuItem("File");
saveAsMenuItem = new ToolStripMenuItem("Save As");
saveAsMenuItem.Click += SaveAsMenuItem_Click; // Event Handler
fileMenuItem.DropDownItems.Add(saveAsMenuItem);
// Theme Menu
themeMenuItem = new ToolStripMenuItem("Theme");
lightModeMenuItem = new ToolStripMenuItem("Light Mode");
darkModeMenuItem = new ToolStripMenuItem("Dark Mode");
lightModeMenuItem.Click += LightModeMenuItem_Click; // Event Handler
darkModeMenuItem.Click += DarkModeMenuItem_Click; // Event Handler
themeMenuItem.DropDownItems.Add(lightModeMenuItem);
themeMenuItem.DropDownItems.Add(darkModeMenuItem);
// Clipboard Mode Menu
clipboardModeMenuItem = new ToolStripMenuItem("Clipboard Mode");
clipboardOnMenuItem = new ToolStripMenuItem("On");
clipboardOffMenuItem = new ToolStripMenuItem("Off");
// CheckOnClick özelliğini kullanarak tıklanınca işaretlenmesini sağlıyoruz
clipboardOnMenuItem.CheckOnClick = true;
clipboardOffMenuItem.CheckOnClick = true;
clipboardOffMenuItem.Checked = true; // Başlangıçta "Off" seçili
clipboardOnMenuItem.Click += ClipboardOnMenuItem_Click;
clipboardOffMenuItem.Click += ClipboardOffMenuItem_Click;
clipboardModeMenuItem.DropDownItems.Add(clipboardOnMenuItem);
clipboardModeMenuItem.DropDownItems.Add(clipboardOffMenuItem);
// Load Tags Menu
loadTagsMenuItem = new ToolStripMenuItem("Load Tags");
loadTagsMenuItem.Click += LoadTagsMenuItem_Click; // Event Handler
// Custom Tag Menu
customTagMenuItem = new ToolStripMenuItem("Custom Tag");
addTagMenuItem = new ToolStripMenuItem("Add Tag");
addTagMenuItem.Click += AddTagMenuItem_Click; // Event Handler
// Declare manageTagsMenuItem at class level and initialize
manageTagsMenuItem = new ToolStripMenuItem("Manage Tags");
manageTagsMenuItem.Click += ManageTagsMenuItem_Click; // Event Handler
customTagMenuItem.DropDownItems.Add(addTagMenuItem);
customTagMenuItem.DropDownItems.Add(manageTagsMenuItem);
// MenuStrip'e Menüleri Ekleme
menuStrip.Items.Add(fileMenuItem);
menuStrip.Items.Add(themeMenuItem);
menuStrip.Items.Add(clipboardModeMenuItem);
menuStrip.Items.Add(loadTagsMenuItem); // "Load Tags" Menü Öğesini Ekledik
menuStrip.Items.Add(customTagMenuItem); // "Custom Tag" Menü Öğesini Ekledik
// Form'a MenuStrip'i Ekleme
this.MainMenuStrip = menuStrip;
Controls.Add(menuStrip); // MenuStrip'i ilk olarak ekliyoruz
// Clipboard izleme için Timer
clipboardMonitorTimer = new Timer { Interval = 1000 }; // Her saniyede bir kontrol eder
clipboardMonitorTimer.Tick += ClipboardMonitorTimer_Tick;
// Online status izleme için Timer
onlineStatusTimer = new Timer { Interval = 10000 }; // Her 10 saniyede bir kontrol eder
onlineStatusTimer.Tick += OnlineStatusTimer_Tick;
// Arama Metin Kutusu ve Butonları
txtSearch = CreateTextBox("", 10, menuStrip.Bottom + 6, 200);
btnSearch = CreateButton("Search", txtSearch.Right + 6, txtSearch.Top - 2, 80);
btnClear = CreateButton("Clear", btnSearch.Right + 6, txtSearch.Top - 2, 80);
// Filtreleme CheckBox'ları
chkFilterFavorites = new CheckBox
{
Text = "Show Favorites",
Top = txtSearch.Bottom + 6,
Left = txtSearch.Left,
Width = 120
};
chkFilterFavorites.CheckedChanged += FilterCheckBoxChanged;
Controls.Add(chkFilterFavorites);
chkFilterPinned = new CheckBox
{
Text = "Show Pinned",
Top = txtSearch.Bottom + 6,
Left = chkFilterFavorites.Right + 10,
Width = 120
};
chkFilterPinned.CheckedChanged += FilterCheckBoxChanged;
Controls.Add(chkFilterPinned);
// Initialize DataGridView
dataGridView = new DataGridView
{
Height = 400,
Left = 0,
Top = chkFilterFavorites.Bottom + 10, // DataGridView'i filtrelerin altına yerleştir
Width = this.ClientSize.Width, // Tam pencere genişliğini kapla
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
MultiSelect = true,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize
};
dataGridView.SelectionChanged += DataGridView_SelectionChanged;
dataGridView.CellContentClick += DataGridView_CellContentClick;
dataGridView.CellFormatting += DataGridView_CellFormatting;
dataGridView.ColumnHeaderMouseClick += DataGridView_ColumnHeaderMouseClick; // For sorting
Controls.Add(dataGridView);
// StatusStrip
statusStrip = new StatusStrip();
statusLabel = new ToolStripStatusLabel();
statusStrip.Items.Add(statusLabel);
statusStrip.Dock = DockStyle.Bottom; // StatusStrip'i pencerenin en altına sabitle
Controls.Add(statusStrip);
// Tag CheckedListBox Oluşturma
tagCheckedListBox = new CheckedListBox
{
Width = 400,
CheckOnClick = true,
Left = this.ClientSize.Width - 400, // Pencerenin en sağ kenarına bitişik hizala
Top = dataGridView.Bottom, // DataGridView'in hemen altına yerleştir
Height = statusStrip.Top - dataGridView.Bottom + 10, // DataGridView'den StatusStrip'e kadar olan yüksekliği ayarla
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
Visible = false // Başlangıçta gizli
};
tagCheckedListBox.ItemCheck += TagCheckedListBox_ItemCheck;
Controls.Add(tagCheckedListBox);
// Kontroller arası standart Windows Forms arayüzündeki gibi mesafeler
int labelLeft = 10;
int textBoxLeft = 150;
int startTop = dataGridView.Bottom + 6; // DataGridView'in hemen altından başla
int textBoxWidth = 300;
int textBoxHeight = 25;
int verticalSpacing = 6; // Kontroller arası boşluk
int currentTop = dataGridView.Bottom + 6; // Kontrolleri DataGridView'in altına yerleştir
// Etiketleri ve Metin Kutularını Oluştur
// Category
CreateLabel("Category:", labelLeft, currentTop);
txtCategory = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Title
CreateLabel("Title:", labelLeft, currentTop);
txtTitle = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Content
CreateLabel("Content:", labelLeft, currentTop);
txtContent = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Background Color
CreateLabel("Background Color:", labelLeft, currentTop);
txtBackgroundColor = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// İşlevsel CheckBox'lar
chkFavorite = new CheckBox { Text = "Favorite", Top = currentTop, Left = textBoxLeft, Width = 80 };
Controls.Add(chkFavorite);
chkPinned = new CheckBox { Text = "Pinned", Top = currentTop, Left = textBoxLeft + 90, Width = 80 };
Controls.Add(chkPinned);
currentTop += chkFavorite.Height + verticalSpacing;
// Butonları Yatay Olarak Hizala
int buttonTop = currentTop + 10; // Biraz boşluk ekle
int buttonLeft = labelLeft;
btnAdd = CreateButton("Add", buttonLeft, buttonTop, 100);
buttonLeft += 110;
btnUpdate = CreateButton("Update", buttonLeft, buttonTop, 100);
buttonLeft += 110;
btnDelete = CreateButton("Delete", buttonLeft, buttonTop, 100);
// ContextMenuStrip for column visibility
columnContextMenu = new ContextMenuStrip();
// Not: Kolon bilgisi mevcut olduktan sonra ContextMenuStrip'i dolduracağız
// Assign the MouseUp event to show the ContextMenuStrip on right-click
dataGridView.MouseUp += DataGridView_MouseUp;
// Event Handler for Form Load
this.Load += MainForm_Load;
// Formun MouseDown olayını abone olma
this.MouseDown += MainForm_MouseDown;
// Tüm kontrollerin MouseDown olaylarını abone et
RegisterMouseDownEvent(this);
}
private void MainForm_MouseDown(object sender, MouseEventArgs e)
{
// Fare imlecinin altındaki kontrolü al
Control clickedControl = this.GetChildAtPoint(e.Location);
// Eğer tıklanan kontrol null ise veya formun kendisiyse, metin kutularını sıfırla
if (clickedControl == null || clickedControl == this)
{
ClearInputs();
}
}
private void RegisterMouseDownEvent(Control parent)
{
foreach (Control control in parent.Controls)
{
control.MouseDown += Control_MouseDown;
if (control.HasChildren)
{
RegisterMouseDownEvent(control);
}
}
}
private void Control_MouseDown(object sender, MouseEventArgs e)
{
// Kontrolün MouseDown olayını formun MouseDown olayına yönlendir
MainForm_MouseDown(sender, e);
}
// Yeni bir CreateLabel metodu ekliyoruz
private Label CreateLabel(string text, int left, int top)
{
Label label = new Label
{
Text = text,
Left = left,
Top = top,
AutoSize = true
};
Controls.Add(label);
return label;
}
/// <summary>
/// Creates a TextBox.
/// </summary>
private TextBox CreateTextBox(string text, int left, int top, int width)
{
TextBox textBox = new TextBox
{
Left = left,
Top = top,
Width = width,
Text = text,
ForeColor = Color.Black
};
Controls.Add(textBox);
return textBox;
}
/// <summary>
/// Creates a Button with specified properties and assigns click event.
/// </summary>
private Button CreateButton(string text, int left, int top, int width)
{
Button button = new Button
{
Text = text,
Left = left,
Top = top,
Width = width
};
button.Click += (sender, e) => ButtonClick(text);
Controls.Add(button);
return button;
}
/// <summary>
/// Handles button click events by identifying the button text.
/// </summary>
private void ButtonClick(string buttonText)
{
switch (buttonText)
{
case "Add":
AddData();
break;
case "Update":
UpdateData();
break;
case "Delete":
DeleteData();
break;
case "Search":
SearchData();
break;
case "Clear":
ClearSearch();
break;
}
}
/// <summary>
/// Handles the "Save As" menu item click to save data in selected format.
/// </summary>
private void SaveAsMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "JSON files (*.json)|*.json|CSV files (*.csv)|*.csv|TSV files (*.tsv)|*.tsv|Text files (*.txt)|*.txt",
Title = "Save As"
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
string extension = Path.GetExtension(saveFileDialog.FileName).ToLower();
// DataGridView'den verileri al
var data = (List<FlashcardItem>)dataGridView.DataSource;
// Verileri seçilen formata göre kaydet
switch (extension)
{
case ".json":
SaveDataAsJSon(data, saveFileDialog.FileName);
break;
case ".csv":
SaveDataAsCsv(data, saveFileDialog.FileName, ',');
break;
case ".tsv":
SaveDataAsCsv(data, saveFileDialog.FileName, '\t');
break;
case ".txt":
SaveDataAsTxt(data, saveFileDialog.FileName);
break;
default:
MessageBox.Show("Unsupported file format.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
MessageBox.Show("Data saved successfully!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Saves data as a JSON file.
/// </summary>
private void SaveDataAsJSon(List<FlashcardItem> data, string filePath)
{
try
{
var options = new JsonSerializerSettings
{
Formatting = Formatting.Indented // JSON'u okunabilir şekilde biçimlendirir
};
string jsonString = JsonConvert.SerializeObject(data, options);
File.WriteAllText(filePath, jsonString, Encoding.UTF8);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving JSON data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Saves data as CSV or TSV file.
/// </summary>
private void SaveDataAsCsv(List<FlashcardItem> data, string filePath, char delimiter)
{
StringBuilder sb = new StringBuilder();
// Başlık satırı
sb.AppendLine(string.Join(delimiter.ToString(), "Category", "Title", "Content", "Favorite", "Pinned", "Timestamp", "BackgroundColor"));
foreach (var item in data)
{
string[] fields = new string[]
{
EscapeCsvField(item.category),
EscapeCsvField(item.title),
EscapeCsvField(item.content),
item.favorite.ToString(),
item.pinned.ToString(),
item.timestamp.ToString(),
EscapeCsvField(item.backgroundColor)
};
sb.AppendLine(string.Join(delimiter.ToString(), fields));
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// Saves data as TXT file.
/// </summary>
private void SaveDataAsTxt(List<FlashcardItem> data, string filePath)
{
StringBuilder sb = new StringBuilder();
foreach (var item in data)
{
sb.AppendLine($"Category: {item.category}");
sb.AppendLine($"Title: {item.title}");
sb.AppendLine($"Content: {item.content}");
sb.AppendLine($"Favorite: {item.favorite}");
sb.AppendLine($"Pinned: {item.pinned}");
sb.AppendLine($"Timestamp: {item.timestamp}");
sb.AppendLine($"BackgroundColor: {item.backgroundColor}");
sb.AppendLine(new string('-', 50));
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// Escapes CSV fields containing delimiter or quotes.
/// </summary>
private string EscapeCsvField(string field)
{
if (field.Contains(",") || field.Contains("\"") || field.Contains("\r") || field.Contains("\n"))
{
return $"\"{field.Replace("\"", "\"\"")}\"";
}
else
{
return field;
}
}
/// <summary>
/// Handles the Light Mode menu item click.
/// </summary>
private void LightModeMenuItem_Click(object sender, EventArgs e)
{
SetTheme(Color.White, Color.Black, false);
}
/// <summary>
/// Handles the Dark Mode menu item click.
/// </summary>
private void DarkModeMenuItem_Click(object sender, EventArgs e)
{
SetTheme(Color.FromArgb(45, 45, 48), Color.White, true);
}
/// <summary>
/// Sets the application's theme colors.
/// </summary>
private void SetTheme(Color backColor, Color foreColor, bool isDarkMode)
{
this.BackColor = backColor;
this.ForeColor = foreColor;
foreach (Control control in this.Controls)
{
SetControlTheme(control, backColor, foreColor);
}
// DataGridView renklerini ayarla
dataGridView.BackgroundColor = backColor;
dataGridView.DefaultCellStyle.BackColor = backColor;
dataGridView.DefaultCellStyle.ForeColor = foreColor;
// Dark mode ise DataGridView kenarlarını, sütun başlıklarını ve yan seçim alanını portakal rengi yap
if (isDarkMode)
{
dataGridView.GridColor = Color.Orange;
dataGridView.EnableHeadersVisualStyles = false;
dataGridView.ColumnHeadersDefaultCellStyle.BackColor = Color.Orange;
dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;
dataGridView.RowHeadersDefaultCellStyle.BackColor = Color.Orange;
dataGridView.RowHeadersDefaultCellStyle.ForeColor = Color.Black;
// Satır yüksekliğini ve yazı boyutunu ayarla
dataGridView.RowTemplate.Height = 30; // Satır yüksekliği
dataGridView.DefaultCellStyle.Font = new Font("Arial", 12, FontStyle.Regular); // Yazı boyutu
// Satır çizgilerini kaldır
dataGridView.CellBorderStyle = DataGridViewCellBorderStyle.None;
}
else
{
dataGridView.EnableHeadersVisualStyles = true;
dataGridView.GridColor = foreColor;
dataGridView.ColumnHeadersDefaultCellStyle.BackColor = backColor;
dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = foreColor;
dataGridView.RowHeadersDefaultCellStyle.BackColor = backColor;
dataGridView.RowHeadersDefaultCellStyle.ForeColor = foreColor;
}
}
/// <summary>
/// Recursively sets theme colors for controls.
/// </summary>
private void SetControlTheme(Control control, Color backColor, Color foreColor)
{
control.BackColor = backColor;
control.ForeColor = foreColor;
if (control.HasChildren)
{
foreach (Control child in control.Controls)
{
SetControlTheme(child, backColor, foreColor);
}
}
}
/// <summary>
/// Handles CellContentClick events for CheckBox columns to toggle favorite and pinned statuses.
/// </summary>
private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && (dataGridView.Columns[e.ColumnIndex].Name == "favorite" || dataGridView.Columns[e.ColumnIndex].Name == "pinned"))
{
var flashcard = (FlashcardItem)dataGridView.Rows[e.RowIndex].DataBoundItem;
bool newValue = !(bool)dataGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value;
if (dataGridView.Columns[e.ColumnIndex].Name == "favorite")
{
flashcard.favorite = newValue;
}
else
{
flashcard.pinned = newValue;
}
_ = UpdateFlashcardInDatabase(flashcard);
}
}
/// <summary>
/// Updates a flashcard in the Firebase database and local storage.
/// </summary>
private async Task UpdateFlashcardInDatabase(FlashcardItem flashcard)
{
try
{
// Update local data
var index = allFlashcards.FindIndex(f => f.id == flashcard.id);
if (index >= 0)
{
allFlashcards[index] = flashcard;
SaveLocalData(); // Save to local storage
}
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(flashcard.id)
.PutAsync(flashcard);
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Update, Flashcard = flashcard });
}
ApplyFiltersAndSort();
}
catch (Exception ex)
{
MessageBox.Show($"Error updating flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private async Task LoadDataAsync()
{
try
{
if (isOnline)
{
// We assume that real-time listener will update the data
// So we don't fetch all data here
}
// After data is loaded, populate the ContextMenuStrip
PopulateColumnContextMenu();
}
catch (Exception ex)
{
MessageBox.Show($"Error loading data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ApplyFiltersAndSort()
{
// Başlangıç olarak tüm flashcard'ları al
IEnumerable<FlashcardItem> filtered = allFlashcards;
// Favoriler filtresini uygula
if (chkFilterFavorites.Checked)
{
filtered = filtered.Where(f => f.favorite);
}
// Sabitlenmiş filtresini uygula
if (chkFilterPinned.Checked)
{
filtered = filtered.Where(f => f.pinned);
}
// Arama filtresini uygula
if (!string.IsNullOrWhiteSpace(currentSearchTerm))
{
filtered = filtered.Where(f =>
f.title.ToLower().Contains(currentSearchTerm) ||
f.category.ToLower().Contains(currentSearchTerm) ||
f.content.ToLower().Contains(currentSearchTerm));
}
// Listeye dönüştür
List<FlashcardItem> filteredList = filtered.ToList();
// Sıralama işlemini uygula
if (!string.IsNullOrEmpty(currentSortColumn))
{
if (sortAscending)
{
switch (currentSortColumn)
{
case "title":
filteredList = filteredList.OrderBy(f => f.title).ToList();
break;
case "timestamp":
filteredList = filteredList.OrderBy(f => f.timestamp).ToList();
break;
}
}
else
{
switch (currentSortColumn)
{
case "title":
filteredList = filteredList.OrderByDescending(f => f.title).ToList();
break;
case "timestamp":
filteredList = filteredList.OrderByDescending(f => f.timestamp).ToList();
break;
}
}
}
UpdateDataGridView(filteredList);
}
/// <summary>
/// Updates the DataGridView with the provided list of flashcards.
/// </summary>
private void UpdateDataGridView(List<FlashcardItem> flashcards)
{
// SelectionChanged olayını geçici olarak devre dışı bırak
dataGridView.SelectionChanged -= DataGridView_SelectionChanged;
// DataGridView'i temizlemeden önce mevcut sütunları ve görünümleri kontrol et
var columnSettings = new Dictionary<string, bool>();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
columnSettings[column.Name] = column.Visible;
}
dataGridView.DataSource = null;
dataGridView.Columns.Clear();
if (flashcards.Any())
{
dataGridView.AutoGenerateColumns = false;
// Define DataGridView columns
// ID Column - Hidden
var idColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "id",
HeaderText = "ID",
Name = "id",
Visible = false
};
dataGridView.Columns.Add(idColumn);
// Category Column
var categoryColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "category",
HeaderText = "Category",
Name = "category",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(categoryColumn);
// Title Column
var titleColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "title",
HeaderText = "Title",
Name = "title",
SortMode = DataGridViewColumnSortMode.Programmatic
};
dataGridView.Columns.Add(titleColumn);
// Content Column
var contentColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "content",
HeaderText = "Content",
Name = "content",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(contentColumn);
// Favorite Column (Checkbox)
var favoriteColumn = new DataGridViewCheckBoxColumn
{
DataPropertyName = "favorite",
HeaderText = "Favorite",
Name = "favorite",
SortMode = DataGridViewColumnSortMode.Automatic
};
dataGridView.Columns.Add(favoriteColumn);
// Pinned Column (Checkbox)
var pinnedColumn = new DataGridViewCheckBoxColumn
{
DataPropertyName = "pinned",
HeaderText = "Pinned",
Name = "pinned",
SortMode = DataGridViewColumnSortMode.Automatic
};
dataGridView.Columns.Add(pinnedColumn);
// Timestamp Column
var timestampColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "timestamp",
HeaderText = "Timestamp",
Name = "timestamp",
SortMode = DataGridViewColumnSortMode.Programmatic
};
dataGridView.Columns.Add(timestampColumn);
// BackgroundColor Column
var backgroundColorColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "backgroundColor",
HeaderText = "Background Color",
Name = "backgroundColor",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(backgroundColorColumn);
dataGridView.DataSource = flashcards;
dataGridView.ClearSelection(); // Seçimi temizle
// Sütun görünürlüklerini geri yükle
foreach (DataGridViewColumn column in dataGridView.Columns)
{
if (columnSettings.ContainsKey(column.Name))
{
column.Visible = columnSettings[column.Name];
}
}
}
else
{
// If no flashcards match the filters, display a message
var messageColumn = new DataGridViewTextBoxColumn
{
HeaderText = "Message",
Name = "Message",
AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill
};
dataGridView.Columns.Add(messageColumn);
dataGridView.Rows.Add("No flashcards found.");
}
// Restore column visibility state
RestoreColumnVisibilityState();
// Update the status label with the count
statusLabel.Text = $"Total Items: {flashcards.Count}";
// SelectionChanged olayını tekrar etkinleştir
dataGridView.SelectionChanged += DataGridView_SelectionChanged;
}
/// <summary>
/// Populates the ContextMenuStrip with the current DataGridView columns.
/// Ensures no duplicate menu items are added.
/// </summary>
private void PopulateColumnContextMenu()
{
columnContextMenu.Items.Clear(); // Clear existing items to prevent duplication
foreach (DataGridViewColumn column in dataGridView.Columns)
{
// Skip hidden or irrelevant columns if necessary
// For example, you might want to skip the "ID" column
if (column.Name == "id") continue;
// Create a ToolStripMenuItem for the column
var menuItem = new ToolStripMenuItem(column.HeaderText)
{
Checked = column.Visible,
CheckOnClick = true,
Tag = column // Store the column in Tag for reference
};
menuItem.CheckedChanged += ColumnMenuItem_CheckedChanged;
// Add the menu item to the ContextMenuStrip
columnContextMenu.Items.Add(menuItem);
}
}
/// <summary>
/// Handles the CheckedChanged event of ContextMenuStrip items to toggle column visibility.
/// </summary>
private void ColumnMenuItem_CheckedChanged(object sender, EventArgs e)
{
if (sender is ToolStripMenuItem menuItem && menuItem.Tag is DataGridViewColumn column)
{
column.Visible = menuItem.Checked;
columnVisibilityState[column.Name] = column.Visible; // Update the visibility state
}
}
/// <summary>
/// Shows the ContextMenuStrip when the user right-clicks on a column header.
/// </summary>
private void DataGridView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var hitTestInfo = dataGridView.HitTest(e.X, e.Y);
if (hitTestInfo.Type == DataGridViewHitTestType.ColumnHeader)
{
// Show the ContextMenuStrip at the mouse position
columnContextMenu.Show(dataGridView, new Point(e.X, e.Y));
}
}
}
/// <summary>
/// Handles the Form Load event to initially load data and start real-time listener.
/// </summary>
private async void MainForm_Load(object sender, EventArgs e)
{
LoadCustomTags(); // Eklendi
// Load local data first
LoadLocalData();
ApplyFiltersAndSort();
// Varsayılan sıralamayı ayarladık
currentSortColumn = "timestamp";
sortAscending = false;
// Start online status timer
onlineStatusTimer.Start();
if (CheckInternetConnection())
{
isOnline = true;
// Start real-time listener if not already started
if (!isListening)
{
RealTimeListener();
}
await ProcessOfflineChanges();
}
else
{
isOnline = false;
}
}
/// <summary>
/// Adds a new flashcard to the Firebase database and local storage.
/// </summary>
private async void AddData()
{
var newFlashcard = CreateFlashcardFromInputs();
if (newFlashcard != null)
{
try
{
// Check if the flashcard already exists to prevent duplicate entries
if (IsDuplicateFlashcard(newFlashcard))
{
MessageBox.Show("This flashcard already exists.", "Duplicate Entry", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
newFlashcard.id = Guid.NewGuid().ToString();
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(newFlashcard.id)
.PutAsync(newFlashcard);
// Do not add to allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Add, Flashcard = newFlashcard });
// Update local data
allFlashcards.Add(newFlashcard);
SaveLocalData();
ApplyFiltersAndSort();
}
ClearInputs();
}
catch (Exception ex)
{
MessageBox.Show($"Error adding flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Checks if a flashcard with the same content already exists.
/// </summary>
private bool IsDuplicateFlashcard(FlashcardItem newFlashcard)
{
return allFlashcards.Any(f =>
f.title == newFlashcard.title &&
f.category == newFlashcard.category &&
f.content == newFlashcard.content);
}
/// <summary>
/// Updates the selected flashcard in the Firebase database and local storage.
/// </summary>
private async void UpdateData()
{
if (dataGridView.SelectedRows.Count > 0)
{
var selectedFlashcard = (FlashcardItem)dataGridView.SelectedRows[0].DataBoundItem;
var updatedFlashcard = CreateFlashcardFromInputs();
if (updatedFlashcard != null)
{
updatedFlashcard.id = selectedFlashcard.id;
updatedFlashcard.timestamp = selectedFlashcard.timestamp; // Zaman damgasını koru
// Check for duplicates excluding the current flashcard
if (IsDuplicateFlashcardExcludingCurrent(updatedFlashcard))
{
MessageBox.Show("A flashcard with the same content already exists.", "Duplicate Entry", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
try
{
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(updatedFlashcard.id)
.PutAsync(updatedFlashcard);
// Do not update allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Update, Flashcard = updatedFlashcard });
// Update local data
var index = allFlashcards.FindIndex(f => f.id == updatedFlashcard.id);
if (index >= 0)
{
allFlashcards[index] = updatedFlashcard;
SaveLocalData();
ApplyFiltersAndSort();
}
}
ClearInputs();
}
catch (Exception ex)
{
MessageBox.Show($"Error updating flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
else
{
MessageBox.Show("Please select a flashcard to update.", "No Selection", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
/// <summary>
/// Checks for duplicates excluding the current flashcard being updated.
/// </summary>
private bool IsDuplicateFlashcardExcludingCurrent(FlashcardItem updatedFlashcard)
{
return allFlashcards.Any(f =>
f.id != updatedFlashcard.id &&
f.title == updatedFlashcard.title &&
f.category == updatedFlashcard.category &&
f.content == updatedFlashcard.content);
}
/// <summary>
/// Deletes selected flashcards from the Firebase database and local storage.
/// </summary>
private async void DeleteData()
{
if (dataGridView.SelectedRows.Count > 0)
{
int selectedCount = dataGridView.SelectedRows.Count;
var confirmResult = MessageBox.Show($"Are you sure you want to delete {selectedCount} flashcard(s)?",
"Confirm Delete",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
try
{
var idsToDelete = dataGridView.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => ((FlashcardItem)row.DataBoundItem).id)
.ToList();
if (isOnline)
{
// Create a list of delete tasks
var deleteTasks = idsToDelete
.Select(id => firebaseClient.Child("flashcards").Child(id).DeleteAsync())
.ToList();
// Execute all delete tasks in parallel
await Task.WhenAll(deleteTasks);
// Do not remove from allFlashcards; real-time listener will handle it
}
else
{
// Add to offline changes
foreach (var id in idsToDelete)
{
var flashcard = allFlashcards.FirstOrDefault(f => f.id == id);
if (flashcard != null)
{
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Delete, Flashcard = flashcard });
}
}
// Remove from local data
allFlashcards.RemoveAll(f => idsToDelete.Contains(f.id));
SaveLocalData();
ApplyFiltersAndSort();
}
// Clear inputs
ClearInputs();
MessageBox.Show($"{selectedCount} flashcard(s) deleted successfully!",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error deleting flashcards: {ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
else
{
MessageBox.Show("Please select one or more flashcards to delete.",
"No Selection",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
}
/// <summary>
/// Creates a FlashcardItem object from the input fields.
/// </summary>
private FlashcardItem CreateFlashcardFromInputs()
{
string title = txtTitle.Text.Trim();
string content = txtContent.Text.Trim();
string backgroundColor = txtBackgroundColor.Text.Trim();
// Kategori belirleme
string category;
if (!string.IsNullOrWhiteSpace(selectedCustomTag))
{
category = selectedCustomTag;
}
else if (!string.IsNullOrWhiteSpace(txtCategory.Text))
{
category = txtCategory.Text.Trim();
}
else
{
category = "uncategorized";
}
return new FlashcardItem
{
title = title,
category = category,
content = content,
favorite = chkFavorite.Checked,
pinned = chkPinned.Checked,
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
backgroundColor = backgroundColor
};
}
/// <summary>
/// Clears the input fields.
/// </summary>
private void ClearInputs()
{
txtTitle.Text = "";
txtCategory.Text = "";
txtContent.Text = "";
txtBackgroundColor.Text = "";
chkFavorite.Checked = false;
chkPinned.Checked = false;
}
/// <summary>
/// Handles the selection change in the DataGridView to populate the input fields.
/// </summary>
private void DataGridView_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView.SelectedRows.Count > 0)
{
var selectedFlashcard = (FlashcardItem)dataGridView.SelectedRows[0].DataBoundItem;
txtTitle.Text = selectedFlashcard.title;
txtTitle.ForeColor = Color.Black;
txtCategory.Text = selectedFlashcard.category;
txtCategory.ForeColor = Color.Black;
txtContent.Text = selectedFlashcard.content;
txtContent.ForeColor = Color.Black;
txtBackgroundColor.Text = selectedFlashcard.backgroundColor;
txtBackgroundColor.ForeColor = Color.Black;
chkFavorite.Checked = selectedFlashcard.favorite;
chkPinned.Checked = selectedFlashcard.pinned;
}
else
{
// Seçim yoksa metin kutularını temizle
ClearInputs();
}
}
/// <summary>
/// Sets up a real-time listener to Firebase to update data upon changes.
/// </summary>
private void RealTimeListener()
{
isListening = true;
firebaseClient
.Child("flashcards")
.AsObservable<FlashcardItem>()
.Subscribe(d =>
{
if (d.Object != null)
{
HandleRealTimeUpdate(d.Key, d.EventType, d.Object);
}
else if (d.EventType == FirebaseEventType.Delete)
{
HandleRealTimeUpdate(d.Key, d.EventType, null);
}
});
}
/// <summary>
/// Handles real-time updates from Firebase.
/// </summary>
/// <param name="key">Firebase key</param>
/// <param name="eventType">Event Type</param>
/// <param name="flashcard">The updated flashcard item.</param>
private void HandleRealTimeUpdate(string key, FirebaseEventType eventType, FlashcardItem flashcard)
{
// Ensure that the id is set properly
if (flashcard != null)
{
flashcard.id = key;
}
Invoke((Action)(() =>
{
var index = allFlashcards.FindIndex(f => f.id == key);
if (eventType == FirebaseEventType.Delete)
{
if (index >= 0)
{
allFlashcards.RemoveAt(index);
SaveLocalData();
ApplyFiltersAndSort();
}
}
else
{
if (index >= 0)
{
// Update existing item
allFlashcards[index] = flashcard;
}
else
{
// Add new item only if it doesn't exist
allFlashcards.Add(flashcard);
}
SaveLocalData();
ApplyFiltersAndSort();
}
}));
}
private void SaveLocalData()
{
try
{
string json = JsonConvert.SerializeObject(allFlashcards);
File.WriteAllText(localDataFilePath, json);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving local data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadLocalData()
{
if (File.Exists(localDataFilePath))
{
try
{
string json = File.ReadAllText(localDataFilePath);
allFlashcards = JsonConvert.DeserializeObject<List<FlashcardItem>>(json);
}
catch (Exception ex)
{
MessageBox.Show($"Error loading local data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
allFlashcards = new List<FlashcardItem>();
}
}
/// <summary>
/// Checks if there is an active internet connection.
/// </summary>
/// <returns>True if online, otherwise false.</returns>
private bool CheckInternetConnection()
{
try
{
using (var client = new System.Net.WebClient())
using (client.OpenRead("http://clients3.google.com/generate_204"))
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Handles the Search button click to apply search criteria.
/// </summary>
private void SearchData()
{
string searchText = txtSearch.Text;
if (string.IsNullOrWhiteSpace(searchText))
{
currentSearchTerm = "";
}
else
{
currentSearchTerm = searchText.ToLower();
}
ApplyFiltersAndSort();
}
/// <summary>
/// Clears the search input and resets search filter.
/// </summary>
private void ClearSearch()
{
txtSearch.Text = "";
currentSearchTerm = "";
ApplyFiltersAndSort();
}
/// <summary>
/// Formats the timestamp column to display human-readable dates.
/// </summary>
private void DataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
if (dataGridView.Columns[e.ColumnIndex].Name == "timestamp")
{
if (e.Value != null && long.TryParse(e.Value.ToString(), out long timestamp))
{
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
DateTime dateTime = dateTimeOffset.ToLocalTime().DateTime;
e.Value = dateTime.ToString("yyyy-MM-dd HH:mm:ss");
e.FormattingApplied = true;
}
}
}
/// <summary>
/// Handles the column header click event to apply sorting.
/// </summary>
private void DataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
string columnName = dataGridView.Columns[e.ColumnIndex].Name;
// Only allow sorting on 'title' and 'timestamp' columns
if (columnName == "title" || columnName == "timestamp")
{
// Toggle sort direction if the same column is clicked
if (currentSortColumn == columnName)
{
sortAscending = !sortAscending;
}
else
{
// Default to ascending if a new column is clicked
currentSortColumn = columnName;
sortAscending = true;
}
// Apply sorting and refresh DataGridView
ApplyFiltersAndSort();
// Reset sort glyphs on all columns
foreach (DataGridViewColumn column in dataGridView.Columns)
{
column.HeaderCell.SortGlyphDirection = SortOrder.None;
}
// Set sort glyph on the sorted column
dataGridView.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = sortAscending ? SortOrder.Ascending : SortOrder.Descending;
}
}
/// <summary>
/// Handles CheckedChanged events for filtering CheckBoxes.
/// </summary>
private void FilterCheckBoxChanged(object sender, EventArgs e)
{
ApplyFiltersAndSort();
}
/// <summary>
/// Restores the column visibility state from the stored dictionary.
/// </summary>
private void RestoreColumnVisibilityState()
{
foreach (DataGridViewColumn column in dataGridView.Columns)
{
if (columnVisibilityState.TryGetValue(column.Name, out bool isVisible))
{
column.Visible = isVisible;
}
}
}
/// <summary>
/// Handles MouseDown event on the background panel to clear inputs.
/// </summary>
private void BackgroundPanel_MouseDown(object sender, MouseEventArgs e)
{
ClearInputs();
}
// Yeni Eklenen Metodlar ve Event Handler'lar
private void ClipboardOnMenuItem_Click(object sender, EventArgs e)
{
StartClipboardMonitoring();
tagCheckedListBox.Visible = true; // Clipboard modu açılırken görünür yap
}
private void ClipboardOffMenuItem_Click(object sender, EventArgs e)
{
StopClipboardMonitoring();
tagCheckedListBox.Visible = false; // Clipboard modu kapanırken gizle
}
private void StartClipboardMonitoring()
{
clipboardOnMenuItem.Checked = true;
clipboardOffMenuItem.Checked = false;
// Panodaki mevcut metni alarak lastClipboardText'e atıyoruz
if (Clipboard.ContainsText())
{
lastClipboardText = Clipboard.GetText();
}
else
{
lastClipboardText = null;
}
clipboardMonitorTimer.Start();
}
private void StopClipboardMonitoring()
{
clipboardOnMenuItem.Checked = false;
clipboardOffMenuItem.Checked = true;
clipboardMonitorTimer.Stop();
}
private async void ClipboardMonitorTimer_Tick(object sender, EventArgs e)
{
await CheckClipboard();
}
private async Task CheckClipboard()
{
try
{
if (Clipboard.ContainsText())
{
string clipboardText = Clipboard.GetText();
if (clipboardText != lastClipboardText)
{
lastClipboardText = clipboardText;
// Metni tek satıra dönüştür
string singleLineText = clipboardText.Replace(Environment.NewLine, " ");
// Kategori belirleme
string category;
if (!string.IsNullOrWhiteSpace(selectedCustomTag))
{
// Eğer Custom Tag altında bir etiket seçildiyse
category = selectedCustomTag;
}
else if (tagCheckedListBox.CheckedItems.Count > 0)
{
// Eğer tagCheckedListBox'dan bir etiket seçildiyse
category = tagCheckedListBox.CheckedItems[0].ToString();
}
else
{
// Hiçbir etiket seçili değilse varsayılan olarak "uncategorized" kullan
category = "uncategorized";
}
// Yeni FlashcardItem oluştur
var newFlashcard = new FlashcardItem
{
title = "", // Başlık boş
category = category, // Belirlenen kategoriyi kullan
content = singleLineText,
favorite = false,
pinned = false,
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
backgroundColor = ""
};
// Veritabanına kaydet
await SaveFlashcardToDatabase(newFlashcard);
}
}
}
catch (Exception ex)
{
// Pano erişiminde hata olursa burası çalışacak
// İsterseniz hata mesajı gösterebilirsiniz
}
}
private async Task SaveFlashcardToDatabase(FlashcardItem flashcard)
{
try
{
// Check if the flashcard already exists to prevent duplicate entries
if (IsDuplicateFlashcard(flashcard))
{
// You can choose to notify the user here if desired
return;
}
flashcard.id = Guid.NewGuid().ToString();
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(flashcard.id)
.PutAsync(flashcard);
// Do not add to allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Add, Flashcard = flashcard });
// Add to local data
allFlashcards.Add(flashcard);
SaveLocalData();
ApplyFiltersAndSort();
}
}
catch (Exception ex)
{
// Hata yönetimi
}
}
private void LoadTagsMenuItem_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
{
folderBrowserDialog.Description = "Klasör Seçin";
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
string selectedFolder = folderBrowserDialog.SelectedPath;
// Klasördeki ve alt klasörlerdeki PDF dosyalarını al
string[] pdfFiles = Directory.GetFiles(selectedFolder, "*.pdf", SearchOption.AllDirectories);
// Dosya isimlerini al (uzantısız)
List<string> tags = pdfFiles.Select(file => Path.GetFileNameWithoutExtension(file)).ToList();
// CheckedListBox'ı güncelle
tagCheckedListBox.Items.Clear();
foreach (var tag in tags)
{
tagCheckedListBox.Items.Add(tag);
}
}
}
}
private void TagCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
// İşaretlenen diğer öğeleri kaldır, sadece bir tanesi seçili olsun
if (e.NewValue == CheckState.Checked)
{
for (int i = 0; i < tagCheckedListBox.Items.Count; i++)
{
if (i != e.Index)
{
tagCheckedListBox.SetItemChecked(i, false);
}
}
selectedTag = tagCheckedListBox.Items[e.Index].ToString();
// Custom Tag seçimini kaldır
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item != addTagMenuItem && item != manageTagsMenuItem)
{
item.Checked = false;
}
}
selectedCustomTag = null;
}
else
{
selectedTag = "uncategorized";
}
}
private void AddTagMenuItem_Click(object sender, EventArgs e)
{
using (Form inputDialog = new Form())
{
inputDialog.Width = 300;
inputDialog.Height = 150;
inputDialog.Text = "Add Custom Tag";
inputDialog.FormBorderStyle = FormBorderStyle.FixedDialog;
inputDialog.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
inputDialog.MinimizeBox = false;
inputDialog.MaximizeBox = false;
inputDialog.AcceptButton = null; // We'll set the accept button later
Label lblTag = new Label()
{
Left = 20,
Top = 20,
Text = "Tag:",
AutoSize = false, // Otomatik boyutlandırmayı kapatıyoruz
Width = 40, // Daha dar bir genişlik veriyoruz
TextAlign = ContentAlignment.TopCenter // Metni sola hizalıyoruz
};
TextBox txtTag = new TextBox() { Left = lblTag.Left + lblTag.Width + 5, Top = lblTag.Top - 3, Width = 150 }; // Metin kutusunu etikete yaklaştırıyoruz
Button btnOK = new Button() { Text = "OK", Left = 50, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
Button btnCancel = new Button() { Text = "Cancel", Left = 150, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
btnOK.Click += (s, args) =>
{
string newTag = txtTag.Text.Trim();
if (!string.IsNullOrEmpty(newTag))
{
// Add tag to customTags list
customTags.Add(newTag);
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Add tag as submenu item under customTagMenuItem
AddCustomTagMenuItem(newTag);
// Close the dialog
inputDialog.DialogResult = DialogResult.OK;
inputDialog.Close();
}
else
{
MessageBox.Show("Tag cannot be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
btnCancel.Click += (s, args) =>
{
inputDialog.DialogResult = DialogResult.Cancel;
inputDialog.Close();
};
inputDialog.Controls.Add(lblTag);
inputDialog.Controls.Add(txtTag);
inputDialog.Controls.Add(btnOK);
inputDialog.Controls.Add(btnCancel);
inputDialog.AcceptButton = btnOK;
inputDialog.ShowDialog(); // Show as modal dialog
}
}
private void AddCustomTagMenuItem(string tag)
{
ToolStripMenuItem tagMenuItem = new ToolStripMenuItem(tag);
tagMenuItem.Checked = false;
tagMenuItem.CheckOnClick = true;
tagMenuItem.Click += TagMenuItem_Click;
// Insert the tag menu item after the "Manage Tags" menu item
int insertIndex = customTagMenuItem.DropDownItems.IndexOf(manageTagsMenuItem) + 1;
customTagMenuItem.DropDownItems.Insert(insertIndex, tagMenuItem);
}
private void TagMenuItem_Click(object sender, EventArgs e)
{
// When a tag is clicked, uncheck other tags
if (sender is ToolStripMenuItem clickedItem)
{
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item != clickedItem && item != addTagMenuItem && item != manageTagsMenuItem)
{
item.Checked = false;
}
}
// Update the selectedCustomTag
if (clickedItem.Checked)
{
selectedCustomTag = clickedItem.Text;
// CheckedListBox'daki tüm seçimleri kaldır
tagCheckedListBox.ItemCheck -= TagCheckedListBox_ItemCheck; // Event geçici olarak devre dışı
for (int i = 0; i < tagCheckedListBox.Items.Count; i++)
{
tagCheckedListBox.SetItemChecked(i, false);
}
tagCheckedListBox.ItemCheck += TagCheckedListBox_ItemCheck; // Event tekrar aktif
selectedTag = "uncategorized";
}
else
{
selectedCustomTag = null;
}
}
}
// Event handler for "Manage Tags" menu item click
private void ManageTagsMenuItem_Click(object sender, EventArgs e)
{
// Open a dialog to manage tags
using (Form manageTagsForm = new Form())
{
manageTagsForm.Width = 300;
manageTagsForm.Height = 400;
manageTagsForm.Text = "Manage Tags";
manageTagsForm.FormBorderStyle = FormBorderStyle.FixedDialog;
manageTagsForm.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
manageTagsForm.MinimizeBox = false;
manageTagsForm.MaximizeBox = false;
ListBox lstTags = new ListBox() { Left = 10, Top = 10, Width = 260, Height = 300 };
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
Button btnEdit = new Button() { Text = "Edit", Left = 10, Width = 80, Top = lstTags.Bottom + 10 };
Button btnDelete = new Button() { Text = "Delete", Left = 100, Width = 80, Top = lstTags.Bottom + 10 };
Button btnClose = new Button() { Text = "Close", Left = 190, Width = 80, Top = lstTags.Bottom + 10 };
btnEdit.Click += (s, args) =>
{
if (lstTags.SelectedItem != null)
{
string selectedTag = lstTags.SelectedItem.ToString();
using (Form editTagForm = new Form())
{
editTagForm.Width = 300;
editTagForm.Height = 150;
editTagForm.Text = "Edit Tag";
editTagForm.FormBorderStyle = FormBorderStyle.FixedDialog;
editTagForm.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
editTagForm.MinimizeBox = false;
editTagForm.MaximizeBox = false;
Label lblTag = new Label()
{
Left = 10,
Top = 20,
Text = "Tag:",
AutoSize = false,
Width = 40, // Daha küçük bir genişlik veriyoruz
TextAlign = ContentAlignment.TopCenter
};
TextBox txtTag = new TextBox() { Left = lblTag.Left + lblTag.Width + 5, Top = lblTag.Top - 3, Width = 150 }; // Metin kutusunu etikete yaklaştırıyoruz
Button btnOK = new Button() { Text = "OK", Left = 50, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
Button btnCancel = new Button() { Text = "Cancel", Left = 150, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
btnOK.Click += (s2, args2) =>
{
string newTag = txtTag.Text.Trim();
if (!string.IsNullOrEmpty(newTag))
{
// Update the tag in customTags list
int index = customTags.IndexOf(selectedTag);
customTags[index] = newTag;
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Update the menu item text
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item.Text == selectedTag)
{
item.Text = newTag;
break;
}
}
// Update selectedCustomTag if this tag was the selected one
if (selectedCustomTag == selectedTag)
{
selectedCustomTag = newTag;
}
// Update the listbox
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
// Close the edit form
editTagForm.DialogResult = DialogResult.OK;
editTagForm.Close();
}
else
{
MessageBox.Show("Tag cannot be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
btnCancel.Click += (s2, args2) =>
{
editTagForm.DialogResult = DialogResult.Cancel;
editTagForm.Close();
};
editTagForm.Controls.Add(lblTag);
editTagForm.Controls.Add(txtTag);
editTagForm.Controls.Add(btnOK);
editTagForm.Controls.Add(btnCancel);
editTagForm.AcceptButton = btnOK;
editTagForm.ShowDialog();
}
}
else
{
MessageBox.Show("Please select a tag to edit.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
};
btnDelete.Click += (s, args) =>
{
if (lstTags.SelectedItem != null)
{
string selectedTag = lstTags.SelectedItem.ToString();
var confirmResult = MessageBox.Show($"Are you sure you want to delete tag '{selectedTag}'?",
"Confirm Delete",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
// Remove from customTags list
customTags.Remove(selectedTag);
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Remove from menu items
ToolStripMenuItem itemToRemove = null;
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item.Text == selectedTag)
{
itemToRemove = item;
break;
}
}
if (itemToRemove != null)
{
customTagMenuItem.DropDownItems.Remove(itemToRemove);
}
// Clear selectedCustomTag if it was deleted
if (selectedCustomTag == selectedTag)
{
selectedCustomTag = null;
}
// Update the listbox
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
}
}
else
{
MessageBox.Show("Please select a tag to delete.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
};
btnClose.Click += (s, args) =>
{
manageTagsForm.Close();
};
manageTagsForm.Controls.Add(lstTags);
manageTagsForm.Controls.Add(btnEdit);
manageTagsForm.Controls.Add(btnDelete);
manageTagsForm.Controls.Add(btnClose);
manageTagsForm.ShowDialog();
}
}
// Custom Tags'i kaydetme metodu
private void SaveCustomTags()
{
try
{
string json = JsonConvert.SerializeObject(customTags);
File.WriteAllText(customTagsFilePath, json);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving custom tags: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Custom Tags'i yükleme metodu
private void LoadCustomTags()
{
if (File.Exists(customTagsFilePath))
{
try
{
string json = File.ReadAllText(customTagsFilePath);
customTags = JsonConvert.DeserializeObject<List<string>>(json);
// customTagMenuItem içindeki mevcut özel etiketleri temizleyelim (Add Tag ve Manage Tags hariç)
for (int i = customTagMenuItem.DropDownItems.Count - 1; i >= 0; i--)
{
var item = customTagMenuItem.DropDownItems[i];
if (item != addTagMenuItem && item != manageTagsMenuItem)
{
customTagMenuItem.DropDownItems.RemoveAt(i);
}
}
// customTags listesindeki etiketleri menüye ekleyelim
foreach (var tag in customTags)
{
AddCustomTagMenuItem(tag);
}
}
catch (Exception ex)
{
MessageBox.Show($"Error loading custom tags: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private async void OnlineStatusTimer_Tick(object sender, EventArgs e)
{
bool currentlyOnline = CheckInternetConnection();
if (currentlyOnline && !isOnline)
{
// We were offline, now online
isOnline = true;
await ProcessOfflineChanges();
// Start real-time listener if not already started
if (!isListening)
{
RealTimeListener();
}
}
else if (!currentlyOnline && isOnline)
{
// We were online, now offline
isOnline = false;
}
}
private async Task ProcessOfflineChanges()
{
foreach (var change in offlineChanges)
{
switch (change.ChangeType)
{
case ChangeType.Add:
// Assume change.Flashcard.id already set
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.PutAsync(change.Flashcard);
break;
case ChangeType.Update:
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.PutAsync(change.Flashcard);
break;
case ChangeType.Delete:
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.DeleteAsync();
break;
}
}
offlineChanges.Clear();
}
}
/// <summary>
/// Represents a Flashcard item.
/// </summary>
public class FlashcardItem
{
public string id { get; set; }
public string category { get; set; }
public string title { get; set; }
public string content { get; set; }
public bool favorite { get; set; }
public bool pinned { get; set; }
public long timestamp { get; set; }
public string backgroundColor { get; set; }
}
public enum ChangeType { Add, Update, Delete }
public class FlashcardChange
{
public ChangeType ChangeType { get; set; }
public FlashcardItem Flashcard { get; set; }
}
}ASSISTANT
Sure! Below is the modified code with `VirtualMode` implemented in the `DataGridView` to improve the application's responsiveness. The changes are carefully integrated to preserve the existing functionality as per your request.
```csharp
using System;
using System.Windows.Forms;
using Firebase.Database;
using Firebase.Database.Query;
using FirebaseAdmin;
using Google.Apis.Auth.OAuth2;
using System.Threading.Tasks;
using System.Linq;
using System.Drawing;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Newtonsoft.Json;
using Firebase.Database.Streaming;
namespace Flashcard
{
public partial class MainForm : Form
{
// Firebase client
private FirebaseClient firebaseClient;
// UI Controls
private DataGridView dataGridView;
private TextBox txtTitle, txtCategory, txtContent, txtBackgroundColor, txtSearch;
private CheckBox chkFavorite, chkPinned;
private CheckBox chkFilterFavorites, chkFilterPinned; // Filtering CheckBoxes
private Button btnAdd, btnUpdate, btnDelete, btnSearch, btnClear;
private StatusStrip statusStrip;
private ToolStripStatusLabel statusLabel;
// Menü Kontrolleri
private MenuStrip menuStrip;
private ToolStripMenuItem fileMenuItem, themeMenuItem;
private ToolStripMenuItem saveAsMenuItem, lightModeMenuItem, darkModeMenuItem;
private ToolStripMenuItem clipboardModeMenuItem, clipboardOnMenuItem, clipboardOffMenuItem;
private ToolStripMenuItem loadTagsMenuItem;
// Custom Tag Menu Items
private ToolStripMenuItem customTagMenuItem;
private ToolStripMenuItem addTagMenuItem;
private ToolStripMenuItem manageTagsMenuItem;
private List<string> customTags = new List<string>();
private string selectedCustomTag = null;
// customTags'i kaydetmek için dosya yolu
private readonly string customTagsFilePath = "custom_tags.json";
// ContextMenuStrip for column visibility
private ContextMenuStrip columnContextMenu;
// Sorting state
private string currentSortColumn = "timestamp";
private bool sortAscending = false;
// Data storage
private List<FlashcardItem> allFlashcards = new List<FlashcardItem>();
private List<FlashcardItem> displayedFlashcards = new List<FlashcardItem>(); // For VirtualMode
// Current search term
private string currentSearchTerm = "";
// Dictionary to store column visibility state
private Dictionary<string, bool> columnVisibilityState = new Dictionary<string, bool>();
// Clipboard monitoring
private Timer clipboardMonitorTimer;
private string lastClipboardText;
// Yeni Eklenen Değişkenler
private CheckedListBox tagCheckedListBox;
private string selectedTag = "uncategorized";
// Local storage file path
private readonly string localDataFilePath = "local_flashcards.json";
// Flag to indicate if application is online
private bool isOnline = false;
// Flag to prevent multiple listeners
private bool isListening = false;
// Offline changes list
private List<FlashcardChange> offlineChanges = new List<FlashcardChange>();
// Timer to check online status
private Timer onlineStatusTimer;
public MainForm()
{
InitializeComponent();
InitializeFirebase();
InitializeUI();
}
/// <summary>
/// Initializes Firebase connection using the provided JSON credentials.
/// </summary>
private void InitializeFirebase()
{
var path = "flashcard-e7f47-firebase-adminsdk-om656-e70a5506c2.json";
var credential = GoogleCredential.FromFile(path);
// Check if FirebaseApp is already created to prevent duplicate initialization
if (FirebaseApp.DefaultInstance == null)
{
FirebaseApp.Create(new AppOptions
{
Credential = credential
});
}
firebaseClient = new FirebaseClient("https://flashcard-e7f47-default-rtdb.europe-west1.firebasedatabase.app/");
}
/// <summary>
/// Sets up the user interface components.
/// </summary>
private void InitializeUI()
{
this.Size = new Size(1000, 800);
this.Text = "Flashcard Application";
this.StartPosition = FormStartPosition.CenterScreen; // MainForm ekranın ortasında açılsın
// MenuStrip Oluşturma
menuStrip = new MenuStrip
{
Dock = DockStyle.Top // MenuStrip'i üstte konumlandır
};
// File Menu
fileMenuItem = new ToolStripMenuItem("File");
saveAsMenuItem = new ToolStripMenuItem("Save As");
saveAsMenuItem.Click += SaveAsMenuItem_Click; // Event Handler
fileMenuItem.DropDownItems.Add(saveAsMenuItem);
// Theme Menu
themeMenuItem = new ToolStripMenuItem("Theme");
lightModeMenuItem = new ToolStripMenuItem("Light Mode");
darkModeMenuItem = new ToolStripMenuItem("Dark Mode");
lightModeMenuItem.Click += LightModeMenuItem_Click; // Event Handler
darkModeMenuItem.Click += DarkModeMenuItem_Click; // Event Handler
themeMenuItem.DropDownItems.Add(lightModeMenuItem);
themeMenuItem.DropDownItems.Add(darkModeMenuItem);
// Clipboard Mode Menu
clipboardModeMenuItem = new ToolStripMenuItem("Clipboard Mode");
clipboardOnMenuItem = new ToolStripMenuItem("On");
clipboardOffMenuItem = new ToolStripMenuItem("Off");
// CheckOnClick özelliğini kullanarak tıklanınca işaretlenmesini sağlıyoruz
clipboardOnMenuItem.CheckOnClick = true;
clipboardOffMenuItem.CheckOnClick = true;
clipboardOffMenuItem.Checked = true; // Başlangıçta "Off" seçili
clipboardOnMenuItem.Click += ClipboardOnMenuItem_Click;
clipboardOffMenuItem.Click += ClipboardOffMenuItem_Click;
clipboardModeMenuItem.DropDownItems.Add(clipboardOnMenuItem);
clipboardModeMenuItem.DropDownItems.Add(clipboardOffMenuItem);
// Load Tags Menu
loadTagsMenuItem = new ToolStripMenuItem("Load Tags");
loadTagsMenuItem.Click += LoadTagsMenuItem_Click; // Event Handler
// Custom Tag Menu
customTagMenuItem = new ToolStripMenuItem("Custom Tag");
addTagMenuItem = new ToolStripMenuItem("Add Tag");
addTagMenuItem.Click += AddTagMenuItem_Click; // Event Handler
// Declare manageTagsMenuItem at class level and initialize
manageTagsMenuItem = new ToolStripMenuItem("Manage Tags");
manageTagsMenuItem.Click += ManageTagsMenuItem_Click; // Event Handler
customTagMenuItem.DropDownItems.Add(addTagMenuItem);
customTagMenuItem.DropDownItems.Add(manageTagsMenuItem);
// MenuStrip'e Menüleri Ekleme
menuStrip.Items.Add(fileMenuItem);
menuStrip.Items.Add(themeMenuItem);
menuStrip.Items.Add(clipboardModeMenuItem);
menuStrip.Items.Add(loadTagsMenuItem); // "Load Tags" Menü Öğesini Ekledik
menuStrip.Items.Add(customTagMenuItem); // "Custom Tag" Menü Öğesini Ekledik
// Form'a MenuStrip'i Ekleme
this.MainMenuStrip = menuStrip;
Controls.Add(menuStrip); // MenuStrip'i ilk olarak ekliyoruz
// Clipboard izleme için Timer
clipboardMonitorTimer = new Timer { Interval = 1000 }; // Her saniyede bir kontrol eder
clipboardMonitorTimer.Tick += ClipboardMonitorTimer_Tick;
// Online status izleme için Timer
onlineStatusTimer = new Timer { Interval = 10000 }; // Her 10 saniyede bir kontrol eder
onlineStatusTimer.Tick += OnlineStatusTimer_Tick;
// Arama Metin Kutusu ve Butonları
txtSearch = CreateTextBox("", 10, menuStrip.Bottom + 6, 200);
btnSearch = CreateButton("Search", txtSearch.Right + 6, txtSearch.Top - 2, 80);
btnClear = CreateButton("Clear", btnSearch.Right + 6, txtSearch.Top - 2, 80);
// Filtreleme CheckBox'ları
chkFilterFavorites = new CheckBox
{
Text = "Show Favorites",
Top = txtSearch.Bottom + 6,
Left = txtSearch.Left,
Width = 120
};
chkFilterFavorites.CheckedChanged += FilterCheckBoxChanged;
Controls.Add(chkFilterFavorites);
chkFilterPinned = new CheckBox
{
Text = "Show Pinned",
Top = txtSearch.Bottom + 6,
Left = chkFilterFavorites.Right + 10,
Width = 120
};
chkFilterPinned.CheckedChanged += FilterCheckBoxChanged;
Controls.Add(chkFilterPinned);
// Initialize DataGridView
dataGridView = new DataGridView
{
Height = 400,
Left = 0,
Top = chkFilterFavorites.Bottom + 10, // DataGridView'i filtrelerin altına yerleştir
Width = this.ClientSize.Width, // Tam pencere genişliğini kapla
Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right,
AllowUserToAddRows = false,
AllowUserToDeleteRows = false,
ReadOnly = false,
SelectionMode = DataGridViewSelectionMode.FullRowSelect,
MultiSelect = true,
AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill,
ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
VirtualMode = true // Enable VirtualMode
};
dataGridView.SelectionChanged += DataGridView_SelectionChanged;
dataGridView.CellContentClick += DataGridView_CellContentClick;
dataGridView.CellFormatting += DataGridView_CellFormatting;
dataGridView.ColumnHeaderMouseClick += DataGridView_ColumnHeaderMouseClick; // For sorting
dataGridView.CellValueNeeded += DataGridView_CellValueNeeded; // For VirtualMode
Controls.Add(dataGridView);
// StatusStrip
statusStrip = new StatusStrip();
statusLabel = new ToolStripStatusLabel();
statusStrip.Items.Add(statusLabel);
statusStrip.Dock = DockStyle.Bottom; // StatusStrip'i pencerenin en altına sabitle
Controls.Add(statusStrip);
// Tag CheckedListBox Oluşturma
tagCheckedListBox = new CheckedListBox
{
Width = 400,
CheckOnClick = true,
Left = this.ClientSize.Width - 400, // Pencerenin en sağ kenarına bitişik hizala
Top = dataGridView.Bottom, // DataGridView'in hemen altına yerleştir
Height = statusStrip.Top - dataGridView.Bottom + 10, // DataGridView'den StatusStrip'e kadar olan yüksekliği ayarla
Anchor = AnchorStyles.Top | AnchorStyles.Bottom | AnchorStyles.Right,
Visible = false // Başlangıçta gizli
};
tagCheckedListBox.ItemCheck += TagCheckedListBox_ItemCheck;
Controls.Add(tagCheckedListBox);
// Kontroller arası standart Windows Forms arayüzündeki gibi mesafeler
int labelLeft = 10;
int textBoxLeft = 150;
int startTop = dataGridView.Bottom + 6; // DataGridView'in hemen altından başla
int textBoxWidth = 300;
int textBoxHeight = 25;
int verticalSpacing = 6; // Kontroller arası boşluk
int currentTop = dataGridView.Bottom + 6; // Kontrolleri DataGridView'in altına yerleştir
// Etiketleri ve Metin Kutularını Oluştur
// Category
CreateLabel("Category:", labelLeft, currentTop);
txtCategory = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Title
CreateLabel("Title:", labelLeft, currentTop);
txtTitle = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Content
CreateLabel("Content:", labelLeft, currentTop);
txtContent = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// Background Color
CreateLabel("Background Color:", labelLeft, currentTop);
txtBackgroundColor = CreateTextBox("", textBoxLeft, currentTop, textBoxWidth);
currentTop += textBoxHeight + verticalSpacing;
// İşlevsel CheckBox'lar
chkFavorite = new CheckBox { Text = "Favorite", Top = currentTop, Left = textBoxLeft, Width = 80 };
Controls.Add(chkFavorite);
chkPinned = new CheckBox { Text = "Pinned", Top = currentTop, Left = textBoxLeft + 90, Width = 80 };
Controls.Add(chkPinned);
currentTop += chkFavorite.Height + verticalSpacing;
// Butonları Yatay Olarak Hizala
int buttonTop = currentTop + 10; // Biraz boşluk ekle
int buttonLeft = labelLeft;
btnAdd = CreateButton("Add", buttonLeft, buttonTop, 100);
buttonLeft += 110;
btnUpdate = CreateButton("Update", buttonLeft, buttonTop, 100);
buttonLeft += 110;
btnDelete = CreateButton("Delete", buttonLeft, buttonTop, 100);
// ContextMenuStrip for column visibility
columnContextMenu = new ContextMenuStrip();
// Not: Kolon bilgisi mevcut olduktan sonra ContextMenuStrip'i dolduracağız
// Assign the MouseUp event to show the ContextMenuStrip on right-click
dataGridView.MouseUp += DataGridView_MouseUp;
// Event Handler for Form Load
this.Load += MainForm_Load;
// Formun MouseDown olayını abone olma
this.MouseDown += MainForm_MouseDown;
// Tüm kontrollerin MouseDown olaylarını abone et
RegisterMouseDownEvent(this);
}
private void MainForm_MouseDown(object sender, MouseEventArgs e)
{
// Fare imlecinin altındaki kontrolü al
Control clickedControl = this.GetChildAtPoint(e.Location);
// Eğer tıklanan kontrol null ise veya formun kendisiyse, metin kutularını sıfırla
if (clickedControl == null || clickedControl == this)
{
ClearInputs();
}
}
private void RegisterMouseDownEvent(Control parent)
{
foreach (Control control in parent.Controls)
{
control.MouseDown += Control_MouseDown;
if (control.HasChildren)
{
RegisterMouseDownEvent(control);
}
}
}
private void Control_MouseDown(object sender, MouseEventArgs e)
{
// Kontrolün MouseDown olayını formun MouseDown olayına yönlendir
MainForm_MouseDown(sender, e);
}
// Yeni bir CreateLabel metodu ekliyoruz
private Label CreateLabel(string text, int left, int top)
{
Label label = new Label
{
Text = text,
Left = left,
Top = top,
AutoSize = true
};
Controls.Add(label);
return label;
}
/// <summary>
/// Creates a TextBox.
/// </summary>
private TextBox CreateTextBox(string text, int left, int top, int width)
{
TextBox textBox = new TextBox
{
Left = left,
Top = top,
Width = width,
Text = text,
ForeColor = Color.Black
};
Controls.Add(textBox);
return textBox;
}
/// <summary>
/// Creates a Button with specified properties and assigns click event.
/// </summary>
private Button CreateButton(string text, int left, int top, int width)
{
Button button = new Button
{
Text = text,
Left = left,
Top = top,
Width = width
};
button.Click += (sender, e) => ButtonClick(text);
Controls.Add(button);
return button;
}
/// <summary>
/// Handles button click events by identifying the button text.
/// </summary>
private void ButtonClick(string buttonText)
{
switch (buttonText)
{
case "Add":
AddData();
break;
case "Update":
UpdateData();
break;
case "Delete":
DeleteData();
break;
case "Search":
SearchData();
break;
case "Clear":
ClearSearch();
break;
}
}
/// <summary>
/// Handles the "Save As" menu item click to save data in selected format.
/// </summary>
private void SaveAsMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog
{
Filter = "JSON files (*.json)|*.json|CSV files (*.csv)|*.csv|TSV files (*.tsv)|*.tsv|Text files (*.txt)|*.txt",
Title = "Save As"
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
string extension = Path.GetExtension(saveFileDialog.FileName).ToLower();
// displayedFlashcards'ı kullan
var data = displayedFlashcards;
// Verileri seçilen formata göre kaydet
switch (extension)
{
case ".json":
SaveDataAsJSon(data, saveFileDialog.FileName);
break;
case ".csv":
SaveDataAsCsv(data, saveFileDialog.FileName, ',');
break;
case ".tsv":
SaveDataAsCsv(data, saveFileDialog.FileName, '\t');
break;
case ".txt":
SaveDataAsTxt(data, saveFileDialog.FileName);
break;
default:
MessageBox.Show("Unsupported file format.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
break;
}
MessageBox.Show("Data saved successfully!", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Saves data as a JSON file.
/// </summary>
private void SaveDataAsJSon(List<FlashcardItem> data, string filePath)
{
try
{
var options = new JsonSerializerSettings
{
Formatting = Formatting.Indented // JSON'u okunabilir şekilde biçimlendirir
};
string jsonString = JsonConvert.SerializeObject(data, options);
File.WriteAllText(filePath, jsonString, Encoding.UTF8);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving JSON data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Saves data as CSV or TSV file.
/// </summary>
private void SaveDataAsCsv(List<FlashcardItem> data, string filePath, char delimiter)
{
StringBuilder sb = new StringBuilder();
// Başlık satırı
sb.AppendLine(string.Join(delimiter.ToString(), "Category", "Title", "Content", "Favorite", "Pinned", "Timestamp", "BackgroundColor"));
foreach (var item in data)
{
string[] fields = new string[]
{
EscapeCsvField(item.category),
EscapeCsvField(item.title),
EscapeCsvField(item.content),
item.favorite.ToString(),
item.pinned.ToString(),
item.timestamp.ToString(),
EscapeCsvField(item.backgroundColor)
};
sb.AppendLine(string.Join(delimiter.ToString(), fields));
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// Saves data as TXT file.
/// </summary>
private void SaveDataAsTxt(List<FlashcardItem> data, string filePath)
{
StringBuilder sb = new StringBuilder();
foreach (var item in data)
{
sb.AppendLine($"Category: {item.category}");
sb.AppendLine($"Title: {item.title}");
sb.AppendLine($"Content: {item.content}");
sb.AppendLine($"Favorite: {item.favorite}");
sb.AppendLine($"Pinned: {item.pinned}");
sb.AppendLine($"Timestamp: {item.timestamp}");
sb.AppendLine($"BackgroundColor: {item.backgroundColor}");
sb.AppendLine(new string('-', 50));
}
File.WriteAllText(filePath, sb.ToString(), Encoding.UTF8);
}
/// <summary>
/// Escapes CSV fields containing delimiter or quotes.
/// </summary>
private string EscapeCsvField(string field)
{
if (field.Contains(",") || field.Contains("\"") || field.Contains("\r") || field.Contains("\n"))
{
return $"\"{field.Replace("\"", "\"\"")}\"";
}
else
{
return field;
}
}
/// <summary>
/// Handles the Light Mode menu item click.
/// </summary>
private void LightModeMenuItem_Click(object sender, EventArgs e)
{
SetTheme(Color.White, Color.Black, false);
}
/// <summary>
/// Handles the Dark Mode menu item click.
/// </summary>
private void DarkModeMenuItem_Click(object sender, EventArgs e)
{
SetTheme(Color.FromArgb(45, 45, 48), Color.White, true);
}
/// <summary>
/// Sets the application's theme colors.
/// </summary>
private void SetTheme(Color backColor, Color foreColor, bool isDarkMode)
{
this.BackColor = backColor;
this.ForeColor = foreColor;
foreach (Control control in this.Controls)
{
SetControlTheme(control, backColor, foreColor);
}
// DataGridView renklerini ayarla
dataGridView.BackgroundColor = backColor;
dataGridView.DefaultCellStyle.BackColor = backColor;
dataGridView.DefaultCellStyle.ForeColor = foreColor;
// Dark mode ise DataGridView kenarlarını, sütun başlıklarını ve yan seçim alanını portakal rengi yap
if (isDarkMode)
{
dataGridView.GridColor = Color.Orange;
dataGridView.EnableHeadersVisualStyles = false;
dataGridView.ColumnHeadersDefaultCellStyle.BackColor = Color.Orange;
dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = Color.Black;
dataGridView.RowHeadersDefaultCellStyle.BackColor = Color.Orange;
dataGridView.RowHeadersDefaultCellStyle.ForeColor = Color.Black;
// Satır yüksekliğini ve yazı boyutunu ayarla
dataGridView.RowTemplate.Height = 30; // Satır yüksekliği
dataGridView.DefaultCellStyle.Font = new Font("Arial", 12, FontStyle.Regular); // Yazı boyutu
// Satır çizgilerini kaldır
dataGridView.CellBorderStyle = DataGridViewCellBorderStyle.None;
}
else
{
dataGridView.EnableHeadersVisualStyles = true;
dataGridView.GridColor = foreColor;
dataGridView.ColumnHeadersDefaultCellStyle.BackColor = backColor;
dataGridView.ColumnHeadersDefaultCellStyle.ForeColor = foreColor;
dataGridView.RowHeadersDefaultCellStyle.BackColor = backColor;
dataGridView.RowHeadersDefaultCellStyle.ForeColor = foreColor;
}
}
/// <summary>
/// Recursively sets theme colors for controls.
/// </summary>
private void SetControlTheme(Control control, Color backColor, Color foreColor)
{
control.BackColor = backColor;
control.ForeColor = foreColor;
if (control.HasChildren)
{
foreach (Control child in control.Controls)
{
SetControlTheme(child, backColor, foreColor);
}
}
}
/// <summary>
/// Handles CellContentClick events for CheckBox columns to toggle favorite and pinned statuses.
/// </summary>
private void DataGridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0 && (dataGridView.Columns[e.ColumnIndex].Name == "favorite" || dataGridView.Columns[e.ColumnIndex].Name == "pinned"))
{
var flashcard = displayedFlashcards[e.RowIndex]; // Adjusted for VirtualMode
bool newValue = !(bool)dataGridView[e.ColumnIndex, e.RowIndex].Value;
if (dataGridView.Columns[e.ColumnIndex].Name == "favorite")
{
flashcard.favorite = newValue;
}
else
{
flashcard.pinned = newValue;
}
_ = UpdateFlashcardInDatabase(flashcard);
}
}
/// <summary>
/// Updates a flashcard in the Firebase database and local storage.
/// </summary>
private async Task UpdateFlashcardInDatabase(FlashcardItem flashcard)
{
try
{
// Update local data
var index = allFlashcards.FindIndex(f => f.id == flashcard.id);
if (index >= 0)
{
allFlashcards[index] = flashcard;
SaveLocalData(); // Save to local storage
}
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(flashcard.id)
.PutAsync(flashcard);
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Update, Flashcard = flashcard });
}
ApplyFiltersAndSort();
}
catch (Exception ex)
{
MessageBox.Show($"Error updating flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private async Task LoadDataAsync()
{
try
{
if (isOnline)
{
// We assume that real-time listener will update the data
// So we don't fetch all data here
}
// After data is loaded, populate the ContextMenuStrip
PopulateColumnContextMenu();
}
catch (Exception ex)
{
MessageBox.Show($"Error loading data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ApplyFiltersAndSort()
{
// Başlangıç olarak tüm flashcard'ları al
IEnumerable<FlashcardItem> filtered = allFlashcards;
// Favoriler filtresini uygula
if (chkFilterFavorites.Checked)
{
filtered = filtered.Where(f => f.favorite);
}
// Sabitlenmiş filtresini uygula
if (chkFilterPinned.Checked)
{
filtered = filtered.Where(f => f.pinned);
}
// Arama filtresini uygula
if (!string.IsNullOrWhiteSpace(currentSearchTerm))
{
filtered = filtered.Where(f =>
f.title.ToLower().Contains(currentSearchTerm) ||
f.category.ToLower().Contains(currentSearchTerm) ||
f.content.ToLower().Contains(currentSearchTerm));
}
// Listeye dönüştür
List<FlashcardItem> filteredList = filtered.ToList();
// Sıralama işlemini uygula
if (!string.IsNullOrEmpty(currentSortColumn))
{
if (sortAscending)
{
switch (currentSortColumn)
{
case "title":
filteredList = filteredList.OrderBy(f => f.title).ToList();
break;
case "timestamp":
filteredList = filteredList.OrderBy(f => f.timestamp).ToList();
break;
}
}
else
{
switch (currentSortColumn)
{
case "title":
filteredList = filteredList.OrderByDescending(f => f.title).ToList();
break;
case "timestamp":
filteredList = filteredList.OrderByDescending(f => f.timestamp).ToList();
break;
}
}
}
UpdateDataGridView(filteredList);
}
/// <summary>
/// Updates the DataGridView with the provided list of flashcards.
/// </summary>
private void UpdateDataGridView(List<FlashcardItem> flashcards)
{
// SelectionChanged olayını geçici olarak devre dışı bırak
dataGridView.SelectionChanged -= DataGridView_SelectionChanged;
// DataGridView'i temizlemeden önce mevcut sütunları ve görünümleri kontrol et
var columnSettings = new Dictionary<string, bool>();
foreach (DataGridViewColumn column in dataGridView.Columns)
{
columnSettings[column.Name] = column.Visible;
}
dataGridView.DataSource = null;
dataGridView.Columns.Clear();
if (flashcards.Any())
{
dataGridView.AutoGenerateColumns = false;
// Define DataGridView columns
// ID Column - Hidden
var idColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "id",
HeaderText = "ID",
Name = "id",
Visible = false
};
dataGridView.Columns.Add(idColumn);
// Category Column
var categoryColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "category",
HeaderText = "Category",
Name = "category",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(categoryColumn);
// Title Column
var titleColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "title",
HeaderText = "Title",
Name = "title",
SortMode = DataGridViewColumnSortMode.Programmatic
};
dataGridView.Columns.Add(titleColumn);
// Content Column
var contentColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "content",
HeaderText = "Content",
Name = "content",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(contentColumn);
// Favorite Column (Checkbox)
var favoriteColumn = new DataGridViewCheckBoxColumn
{
DataPropertyName = "favorite",
HeaderText = "Favorite",
Name = "favorite",
SortMode = DataGridViewColumnSortMode.Automatic
};
dataGridView.Columns.Add(favoriteColumn);
// Pinned Column (Checkbox)
var pinnedColumn = new DataGridViewCheckBoxColumn
{
DataPropertyName = "pinned",
HeaderText = "Pinned",
Name = "pinned",
SortMode = DataGridViewColumnSortMode.Automatic
};
dataGridView.Columns.Add(pinnedColumn);
// Timestamp Column
var timestampColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "timestamp",
HeaderText = "Timestamp",
Name = "timestamp",
SortMode = DataGridViewColumnSortMode.Programmatic
};
dataGridView.Columns.Add(timestampColumn);
// BackgroundColor Column
var backgroundColorColumn = new DataGridViewTextBoxColumn
{
DataPropertyName = "backgroundColor",
HeaderText = "Background Color",
Name = "backgroundColor",
SortMode = DataGridViewColumnSortMode.NotSortable
};
dataGridView.Columns.Add(backgroundColorColumn);
// Assign the filtered flashcards to displayedFlashcards
displayedFlashcards = flashcards;
// Set the RowCount for VirtualMode
dataGridView.RowCount = displayedFlashcards.Count;
dataGridView.ClearSelection(); // Seçimi temizle
// Sütun görünürlüklerini geri yükle
foreach (DataGridViewColumn column in dataGridView.Columns)
{
if (columnSettings.ContainsKey(column.Name))
{
column.Visible = columnSettings[column.Name];
}
}
}
else
{
// If no flashcards match the filters, display a message
dataGridView.Rows.Clear();
displayedFlashcards = new List<FlashcardItem>();
dataGridView.RowCount = 0;
}
// Restore column visibility state
RestoreColumnVisibilityState();
// Update the status label with the count
statusLabel.Text = $"Total Items: {flashcards.Count}";
// SelectionChanged olayını tekrar etkinleştir
dataGridView.SelectionChanged += DataGridView_SelectionChanged;
}
/// <summary>
/// Provides the data for the DataGridView cells in VirtualMode.
/// </summary>
private void DataGridView_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
{
if (e.RowIndex >= 0 && e.RowIndex < displayedFlashcards.Count)
{
var flashcard = displayedFlashcards[e.RowIndex];
var columnName = dataGridView.Columns[e.ColumnIndex].Name;
switch (columnName)
{
case "id":
e.Value = flashcard.id;
break;
case "category":
e.Value = flashcard.category;
break;
case "title":
e.Value = flashcard.title;
break;
case "content":
e.Value = flashcard.content;
break;
case "favorite":
e.Value = flashcard.favorite;
break;
case "pinned":
e.Value = flashcard.pinned;
break;
case "timestamp":
// Format the timestamp here if needed
if (long.TryParse(flashcard.timestamp.ToString(), out long timestamp))
{
DateTimeOffset dateTimeOffset = DateTimeOffset.FromUnixTimeMilliseconds(timestamp);
DateTime dateTime = dateTimeOffset.ToLocalTime().DateTime;
e.Value = dateTime.ToString("yyyy-MM-dd HH:mm:ss");
}
else
{
e.Value = flashcard.timestamp;
}
break;
case "backgroundColor":
e.Value = flashcard.backgroundColor;
break;
}
}
}
/// <summary>
/// Populates the ContextMenuStrip with the current DataGridView columns.
/// Ensures no duplicate menu items are added.
/// </summary>
private void PopulateColumnContextMenu()
{
columnContextMenu.Items.Clear(); // Clear existing items to prevent duplication
foreach (DataGridViewColumn column in dataGridView.Columns)
{
// Skip hidden or irrelevant columns if necessary
// For example, you might want to skip the "ID" column
if (column.Name == "id") continue;
// Create a ToolStripMenuItem for the column
var menuItem = new ToolStripMenuItem(column.HeaderText)
{
Checked = column.Visible,
CheckOnClick = true,
Tag = column // Store the column in Tag for reference
};
menuItem.CheckedChanged += ColumnMenuItem_CheckedChanged;
// Add the menu item to the ContextMenuStrip
columnContextMenu.Items.Add(menuItem);
}
}
/// <summary>
/// Handles the CheckedChanged event of ContextMenuStrip items to toggle column visibility.
/// </summary>
private void ColumnMenuItem_CheckedChanged(object sender, EventArgs e)
{
if (sender is ToolStripMenuItem menuItem && menuItem.Tag is DataGridViewColumn column)
{
column.Visible = menuItem.Checked;
columnVisibilityState[column.Name] = column.Visible; // Update the visibility state
}
}
/// <summary>
/// Shows the ContextMenuStrip when the user right-clicks on a column header.
/// </summary>
private void DataGridView_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
var hitTestInfo = dataGridView.HitTest(e.X, e.Y);
if (hitTestInfo.Type == DataGridViewHitTestType.ColumnHeader)
{
// Show the ContextMenuStrip at the mouse position
columnContextMenu.Show(dataGridView, new Point(e.X, e.Y));
}
}
}
/// <summary>
/// Handles the Form Load event to initially load data and start real-time listener.
/// </summary>
private async void MainForm_Load(object sender, EventArgs e)
{
LoadCustomTags(); // Eklendi
// Load local data first
LoadLocalData();
ApplyFiltersAndSort();
// Varsayılan sıralamayı ayarladık
currentSortColumn = "timestamp";
sortAscending = false;
// Start online status timer
onlineStatusTimer.Start();
if (CheckInternetConnection())
{
isOnline = true;
// Start real-time listener if not already started
if (!isListening)
{
RealTimeListener();
}
await ProcessOfflineChanges();
}
else
{
isOnline = false;
}
}
/// <summary>
/// Adds a new flashcard to the Firebase database and local storage.
/// </summary>
private async void AddData()
{
var newFlashcard = CreateFlashcardFromInputs();
if (newFlashcard != null)
{
try
{
// Check if the flashcard already exists to prevent duplicate entries
if (IsDuplicateFlashcard(newFlashcard))
{
MessageBox.Show("This flashcard already exists.", "Duplicate Entry", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
newFlashcard.id = Guid.NewGuid().ToString();
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(newFlashcard.id)
.PutAsync(newFlashcard);
// Do not add to allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Add, Flashcard = newFlashcard });
// Update local data
allFlashcards.Add(newFlashcard);
SaveLocalData();
ApplyFiltersAndSort();
}
ClearInputs();
}
catch (Exception ex)
{
MessageBox.Show($"Error adding flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
/// <summary>
/// Checks if a flashcard with the same content already exists.
/// </summary>
private bool IsDuplicateFlashcard(FlashcardItem newFlashcard)
{
return allFlashcards.Any(f =>
f.title == newFlashcard.title &&
f.category == newFlashcard.category &&
f.content == newFlashcard.content);
}
/// <summary>
/// Updates the selected flashcard in the Firebase database and local storage.
/// </summary>
private async void UpdateData()
{
if (dataGridView.SelectedRows.Count > 0)
{
int rowIndex = dataGridView.SelectedRows[0].Index;
var selectedFlashcard = displayedFlashcards[rowIndex]; // Adjusted for VirtualMode
var updatedFlashcard = CreateFlashcardFromInputs();
if (updatedFlashcard != null)
{
updatedFlashcard.id = selectedFlashcard.id;
updatedFlashcard.timestamp = selectedFlashcard.timestamp; // Zaman damgasını koru
// Check for duplicates excluding the current flashcard
if (IsDuplicateFlashcardExcludingCurrent(updatedFlashcard))
{
MessageBox.Show("A flashcard with the same content already exists.", "Duplicate Entry", MessageBoxButtons.OK, MessageBoxIcon.Information);
return;
}
try
{
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(updatedFlashcard.id)
.PutAsync(updatedFlashcard);
// Do not update allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Update, Flashcard = updatedFlashcard });
// Update local data
var index = allFlashcards.FindIndex(f => f.id == updatedFlashcard.id);
if (index >= 0)
{
allFlashcards[index] = updatedFlashcard;
SaveLocalData();
ApplyFiltersAndSort();
}
}
ClearInputs();
}
catch (Exception ex)
{
MessageBox.Show($"Error updating flashcard: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
else
{
MessageBox.Show("Please select a flashcard to update.", "No Selection", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
/// <summary>
/// Checks for duplicates excluding the current flashcard being updated.
/// </summary>
private bool IsDuplicateFlashcardExcludingCurrent(FlashcardItem updatedFlashcard)
{
return allFlashcards.Any(f =>
f.id != updatedFlashcard.id &&
f.title == updatedFlashcard.title &&
f.category == updatedFlashcard.category &&
f.content == updatedFlashcard.content);
}
/// <summary>
/// Deletes selected flashcards from the Firebase database and local storage.
/// </summary>
private async void DeleteData()
{
if (dataGridView.SelectedRows.Count > 0)
{
int selectedCount = dataGridView.SelectedRows.Count;
var confirmResult = MessageBox.Show($"Are you sure you want to delete {selectedCount} flashcard(s)?",
"Confirm Delete",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
try
{
var indicesToDelete = dataGridView.SelectedRows
.Cast<DataGridViewRow>()
.Select(row => row.Index)
.ToList();
var idsToDelete = indicesToDelete.Select(index => displayedFlashcards[index].id).ToList();
if (isOnline)
{
// Create a list of delete tasks
var deleteTasks = idsToDelete
.Select(id => firebaseClient.Child("flashcards").Child(id).DeleteAsync())
.ToList();
// Execute all delete tasks in parallel
await Task.WhenAll(deleteTasks);
// Do not remove from allFlashcards; real-time listener will handle it
}
else
{
// Add to offline changes
foreach (var id in idsToDelete)
{
var flashcard = allFlashcards.FirstOrDefault(f => f.id == id);
if (flashcard != null)
{
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Delete, Flashcard = flashcard });
}
}
// Remove from local data
allFlashcards.RemoveAll(f => idsToDelete.Contains(f.id));
SaveLocalData();
ApplyFiltersAndSort();
}
// Clear inputs
ClearInputs();
MessageBox.Show($"{selectedCount} flashcard(s) deleted successfully!",
"Success",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Error deleting flashcards: {ex.Message}",
"Error",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
}
else
{
MessageBox.Show("Please select one or more flashcards to delete.",
"No Selection",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}
}
/// <summary>
/// Creates a FlashcardItem object from the input fields.
/// </summary>
private FlashcardItem CreateFlashcardFromInputs()
{
string title = txtTitle.Text.Trim();
string content = txtContent.Text.Trim();
string backgroundColor = txtBackgroundColor.Text.Trim();
// Kategori belirleme
string category;
if (!string.IsNullOrWhiteSpace(selectedCustomTag))
{
category = selectedCustomTag;
}
else if (!string.IsNullOrWhiteSpace(txtCategory.Text))
{
category = txtCategory.Text.Trim();
}
else
{
category = "uncategorized";
}
return new FlashcardItem
{
title = title,
category = category,
content = content,
favorite = chkFavorite.Checked,
pinned = chkPinned.Checked,
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
backgroundColor = backgroundColor
};
}
/// <summary>
/// Clears the input fields.
/// </summary>
private void ClearInputs()
{
txtTitle.Text = "";
txtCategory.Text = "";
txtContent.Text = "";
txtBackgroundColor.Text = "";
chkFavorite.Checked = false;
chkPinned.Checked = false;
}
/// <summary>
/// Handles the selection change in the DataGridView to populate the input fields.
/// </summary>
private void DataGridView_SelectionChanged(object sender, EventArgs e)
{
if (dataGridView.SelectedRows.Count > 0)
{
int rowIndex = dataGridView.SelectedRows[0].Index;
if (rowIndex >= 0 && rowIndex < displayedFlashcards.Count)
{
var selectedFlashcard = displayedFlashcards[rowIndex];
txtTitle.Text = selectedFlashcard.title;
txtTitle.ForeColor = Color.Black;
txtCategory.Text = selectedFlashcard.category;
txtCategory.ForeColor = Color.Black;
txtContent.Text = selectedFlashcard.content;
txtContent.ForeColor = Color.Black;
txtBackgroundColor.Text = selectedFlashcard.backgroundColor;
txtBackgroundColor.ForeColor = Color.Black;
chkFavorite.Checked = selectedFlashcard.favorite;
chkPinned.Checked = selectedFlashcard.pinned;
}
}
else
{
// Seçim yoksa metin kutularını temizle
ClearInputs();
}
}
/// <summary>
/// Sets up a real-time listener to Firebase to update data upon changes.
/// </summary>
private void RealTimeListener()
{
isListening = true;
firebaseClient
.Child("flashcards")
.AsObservable<FlashcardItem>()
.Subscribe(d =>
{
if (d.Object != null)
{
HandleRealTimeUpdate(d.Key, d.EventType, d.Object);
}
else if (d.EventType == FirebaseEventType.Delete)
{
HandleRealTimeUpdate(d.Key, d.EventType, null);
}
});
}
/// <summary>
/// Handles real-time updates from Firebase.
/// </summary>
/// <param name="key">Firebase key</param>
/// <param name="eventType">Event Type</param>
/// <param name="flashcard">The updated flashcard item.</param>
private void HandleRealTimeUpdate(string key, FirebaseEventType eventType, FlashcardItem flashcard)
{
// Ensure that the id is set properly
if (flashcard != null)
{
flashcard.id = key;
}
Invoke((Action)(() =>
{
var index = allFlashcards.FindIndex(f => f.id == key);
if (eventType == FirebaseEventType.Delete)
{
if (index >= 0)
{
allFlashcards.RemoveAt(index);
SaveLocalData();
ApplyFiltersAndSort();
}
}
else
{
if (index >= 0)
{
// Update existing item
allFlashcards[index] = flashcard;
}
else
{
// Add new item only if it doesn't exist
allFlashcards.Add(flashcard);
}
SaveLocalData();
ApplyFiltersAndSort();
}
}));
}
private void SaveLocalData()
{
try
{
string json = JsonConvert.SerializeObject(allFlashcards);
File.WriteAllText(localDataFilePath, json);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving local data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LoadLocalData()
{
if (File.Exists(localDataFilePath))
{
try
{
string json = File.ReadAllText(localDataFilePath);
allFlashcards = JsonConvert.DeserializeObject<List<FlashcardItem>>(json);
}
catch (Exception ex)
{
MessageBox.Show($"Error loading local data: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
else
{
allFlashcards = new List<FlashcardItem>();
}
}
/// <summary>
/// Checks if there is an active internet connection.
/// </summary>
/// <returns>True if online, otherwise false.</returns>
private bool CheckInternetConnection()
{
try
{
using (var client = new System.Net.WebClient())
using (client.OpenRead("http://clients3.google.com/generate_204"))
return true;
}
catch
{
return false;
}
}
/// <summary>
/// Handles the Search button click to apply search criteria.
/// </summary>
private void SearchData()
{
string searchText = txtSearch.Text;
if (string.IsNullOrWhiteSpace(searchText))
{
currentSearchTerm = "";
}
else
{
currentSearchTerm = searchText.ToLower();
}
ApplyFiltersAndSort();
}
/// <summary>
/// Clears the search input and resets search filter.
/// </summary>
private void ClearSearch()
{
txtSearch.Text = "";
currentSearchTerm = "";
ApplyFiltersAndSort();
}
/// <summary>
/// Formats the timestamp column to display human-readable dates.
/// </summary>
private void DataGridView_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
{
// No longer needed since we format timestamp in CellValueNeeded
}
/// <summary>
/// Handles the column header click event to apply sorting.
/// </summary>
private void DataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
string columnName = dataGridView.Columns[e.ColumnIndex].Name;
// Only allow sorting on 'title' and 'timestamp' columns
if (columnName == "title" || columnName == "timestamp")
{
// Toggle sort direction if the same column is clicked
if (currentSortColumn == columnName)
{
sortAscending = !sortAscending;
}
else
{
// Default to ascending if a new column is clicked
currentSortColumn = columnName;
sortAscending = true;
}
// Apply sorting and refresh DataGridView
ApplyFiltersAndSort();
// Reset sort glyphs on all columns
foreach (DataGridViewColumn column in dataGridView.Columns)
{
column.HeaderCell.SortGlyphDirection = SortOrder.None;
}
// Set sort glyph on the sorted column
dataGridView.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = sortAscending ? SortOrder.Ascending : SortOrder.Descending;
}
}
/// <summary>
/// Handles CheckedChanged events for filtering CheckBoxes.
/// </summary>
private void FilterCheckBoxChanged(object sender, EventArgs e)
{
ApplyFiltersAndSort();
}
/// <summary>
/// Restores the column visibility state from the stored dictionary.
/// </summary>
private void RestoreColumnVisibilityState()
{
foreach (DataGridViewColumn column in dataGridView.Columns)
{
if (columnVisibilityState.TryGetValue(column.Name, out bool isVisible))
{
column.Visible = isVisible;
}
}
}
/// <summary>
/// Handles MouseDown event on the background panel to clear inputs.
/// </summary>
private void BackgroundPanel_MouseDown(object sender, MouseEventArgs e)
{
ClearInputs();
}
// Yeni Eklenen Metodlar ve Event Handler'lar
private void ClipboardOnMenuItem_Click(object sender, EventArgs e)
{
StartClipboardMonitoring();
tagCheckedListBox.Visible = true; // Clipboard modu açılırken görünür yap
}
private void ClipboardOffMenuItem_Click(object sender, EventArgs e)
{
StopClipboardMonitoring();
tagCheckedListBox.Visible = false; // Clipboard modu kapanırken gizle
}
private void StartClipboardMonitoring()
{
clipboardOnMenuItem.Checked = true;
clipboardOffMenuItem.Checked = false;
// Panodaki mevcut metni alarak lastClipboardText'e atıyoruz
if (Clipboard.ContainsText())
{
lastClipboardText = Clipboard.GetText();
}
else
{
lastClipboardText = null;
}
clipboardMonitorTimer.Start();
}
private void StopClipboardMonitoring()
{
clipboardOnMenuItem.Checked = false;
clipboardOffMenuItem.Checked = true;
clipboardMonitorTimer.Stop();
}
private async void ClipboardMonitorTimer_Tick(object sender, EventArgs e)
{
await CheckClipboard();
}
private async Task CheckClipboard()
{
try
{
if (Clipboard.ContainsText())
{
string clipboardText = Clipboard.GetText();
if (clipboardText != lastClipboardText)
{
lastClipboardText = clipboardText;
// Metni tek satıra dönüştür
string singleLineText = clipboardText.Replace(Environment.NewLine, " ");
// Kategori belirleme
string category;
if (!string.IsNullOrWhiteSpace(selectedCustomTag))
{
// Eğer Custom Tag altında bir etiket seçildiyse
category = selectedCustomTag;
}
else if (tagCheckedListBox.CheckedItems.Count > 0)
{
// Eğer tagCheckedListBox'dan bir etiket seçildiyse
category = tagCheckedListBox.CheckedItems[0].ToString();
}
else
{
// Hiçbir etiket seçili değilse varsayılan olarak "uncategorized" kullan
category = "uncategorized";
}
// Yeni FlashcardItem oluştur
var newFlashcard = new FlashcardItem
{
title = "", // Başlık boş
category = category, // Belirlenen kategoriyi kullan
content = singleLineText,
favorite = false,
pinned = false,
timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
backgroundColor = ""
};
// Veritabanına kaydet
await SaveFlashcardToDatabase(newFlashcard);
}
}
}
catch (Exception ex)
{
// Pano erişiminde hata olursa burası çalışacak
// İsterseniz hata mesajı gösterebilirsiniz
}
}
private async Task SaveFlashcardToDatabase(FlashcardItem flashcard)
{
try
{
// Check if the flashcard already exists to prevent duplicate entries
if (IsDuplicateFlashcard(flashcard))
{
// You can choose to notify the user here if desired
return;
}
flashcard.id = Guid.NewGuid().ToString();
if (isOnline)
{
await firebaseClient
.Child("flashcards")
.Child(flashcard.id)
.PutAsync(flashcard);
// Do not add to allFlashcards here; real-time listener will handle it
}
else
{
// Add to offline changes
offlineChanges.Add(new FlashcardChange { ChangeType = ChangeType.Add, Flashcard = flashcard });
// Add to local data
allFlashcards.Add(flashcard);
SaveLocalData();
ApplyFiltersAndSort();
}
}
catch (Exception ex)
{
// Hata yönetimi
}
}
private void LoadTagsMenuItem_Click(object sender, EventArgs e)
{
using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
{
folderBrowserDialog.Description = "Klasör Seçin";
if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
{
string selectedFolder = folderBrowserDialog.SelectedPath;
// Klasördeki ve alt klasörlerdeki PDF dosyalarını al
string[] pdfFiles = Directory.GetFiles(selectedFolder, "*.pdf", SearchOption.AllDirectories);
// Dosya isimlerini al (uzantısız)
List<string> tags = pdfFiles.Select(file => Path.GetFileNameWithoutExtension(file)).ToList();
// CheckedListBox'ı güncelle
tagCheckedListBox.Items.Clear();
foreach (var tag in tags)
{
tagCheckedListBox.Items.Add(tag);
}
}
}
}
private void TagCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
// İşaretlenen diğer öğeleri kaldır, sadece bir tanesi seçili olsun
if (e.NewValue == CheckState.Checked)
{
for (int i = 0; i < tagCheckedListBox.Items.Count; i++)
{
if (i != e.Index)
{
tagCheckedListBox.SetItemChecked(i, false);
}
}
selectedTag = tagCheckedListBox.Items[e.Index].ToString();
// Custom Tag seçimini kaldır
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item != addTagMenuItem && item != manageTagsMenuItem)
{
item.Checked = false;
}
}
selectedCustomTag = null;
}
else
{
selectedTag = "uncategorized";
}
}
private void AddTagMenuItem_Click(object sender, EventArgs e)
{
using (Form inputDialog = new Form())
{
inputDialog.Width = 300;
inputDialog.Height = 150;
inputDialog.Text = "Add Custom Tag";
inputDialog.FormBorderStyle = FormBorderStyle.FixedDialog;
inputDialog.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
inputDialog.MinimizeBox = false;
inputDialog.MaximizeBox = false;
inputDialog.AcceptButton = null; // We'll set the accept button later
Label lblTag = new Label()
{
Left = 20,
Top = 20,
Text = "Tag:",
AutoSize = false, // Otomatik boyutlandırmayı kapatıyoruz
Width = 40, // Daha dar bir genişlik veriyoruz
TextAlign = ContentAlignment.TopCenter // Metni sola hizalıyoruz
};
TextBox txtTag = new TextBox() { Left = lblTag.Left + lblTag.Width + 5, Top = lblTag.Top - 3, Width = 150 }; // Metin kutusunu etikete yaklaştırıyoruz
Button btnOK = new Button() { Text = "OK", Left = 50, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
Button btnCancel = new Button() { Text = "Cancel", Left = 150, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
btnOK.Click += (s, args) =>
{
string newTag = txtTag.Text.Trim();
if (!string.IsNullOrEmpty(newTag))
{
// Add tag to customTags list
customTags.Add(newTag);
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Add tag as submenu item under customTagMenuItem
AddCustomTagMenuItem(newTag);
// Close the dialog
inputDialog.DialogResult = DialogResult.OK;
inputDialog.Close();
}
else
{
MessageBox.Show("Tag cannot be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
btnCancel.Click += (s, args) =>
{
inputDialog.DialogResult = DialogResult.Cancel;
inputDialog.Close();
};
inputDialog.Controls.Add(lblTag);
inputDialog.Controls.Add(txtTag);
inputDialog.Controls.Add(btnOK);
inputDialog.Controls.Add(btnCancel);
inputDialog.AcceptButton = btnOK;
inputDialog.ShowDialog(); // Show as modal dialog
}
}
private void AddCustomTagMenuItem(string tag)
{
ToolStripMenuItem tagMenuItem = new ToolStripMenuItem(tag);
tagMenuItem.Checked = false;
tagMenuItem.CheckOnClick = true;
tagMenuItem.Click += TagMenuItem_Click;
// Insert the tag menu item after the "Manage Tags" menu item
int insertIndex = customTagMenuItem.DropDownItems.IndexOf(manageTagsMenuItem) + 1;
customTagMenuItem.DropDownItems.Insert(insertIndex, tagMenuItem);
}
private void TagMenuItem_Click(object sender, EventArgs e)
{
// When a tag is clicked, uncheck other tags
if (sender is ToolStripMenuItem clickedItem)
{
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item != clickedItem && item != addTagMenuItem && item != manageTagsMenuItem)
{
item.Checked = false;
}
}
// Update the selectedCustomTag
if (clickedItem.Checked)
{
selectedCustomTag = clickedItem.Text;
// CheckedListBox'daki tüm seçimleri kaldır
tagCheckedListBox.ItemCheck -= TagCheckedListBox_ItemCheck; // Event geçici olarak devre dışı
for (int i = 0; i < tagCheckedListBox.Items.Count; i++)
{
tagCheckedListBox.SetItemChecked(i, false);
}
tagCheckedListBox.ItemCheck += TagCheckedListBox_ItemCheck; // Event tekrar aktif
selectedTag = "uncategorized";
}
else
{
selectedCustomTag = null;
}
}
}
// Event handler for "Manage Tags" menu item click
private void ManageTagsMenuItem_Click(object sender, EventArgs e)
{
// Open a dialog to manage tags
using (Form manageTagsForm = new Form())
{
manageTagsForm.Width = 300;
manageTagsForm.Height = 400;
manageTagsForm.Text = "Manage Tags";
manageTagsForm.FormBorderStyle = FormBorderStyle.FixedDialog;
manageTagsForm.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
manageTagsForm.MinimizeBox = false;
manageTagsForm.MaximizeBox = false;
ListBox lstTags = new ListBox() { Left = 10, Top = 10, Width = 260, Height = 300 };
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
Button btnEdit = new Button() { Text = "Edit", Left = 10, Width = 80, Top = lstTags.Bottom + 10 };
Button btnDelete = new Button() { Text = "Delete", Left = 100, Width = 80, Top = lstTags.Bottom + 10 };
Button btnClose = new Button() { Text = "Close", Left = 190, Width = 80, Top = lstTags.Bottom + 10 };
btnEdit.Click += (s, args) =>
{
if (lstTags.SelectedItem != null)
{
string selectedTag = lstTags.SelectedItem.ToString();
using (Form editTagForm = new Form())
{
editTagForm.Width = 300;
editTagForm.Height = 150;
editTagForm.Text = "Edit Tag";
editTagForm.FormBorderStyle = FormBorderStyle.FixedDialog;
editTagForm.StartPosition = FormStartPosition.CenterParent; // Ekranın ortasında açılsın
editTagForm.MinimizeBox = false;
editTagForm.MaximizeBox = false;
Label lblTag = new Label()
{
Left = 10,
Top = 20,
Text = "Tag:",
AutoSize = false,
Width = 40, // Daha küçük bir genişlik veriyoruz
TextAlign = ContentAlignment.TopCenter
};
TextBox txtTag = new TextBox() { Left = lblTag.Left + lblTag.Width + 5, Top = lblTag.Top - 3, Width = 150 }; // Metin kutusunu etikete yaklaştırıyoruz
Button btnOK = new Button() { Text = "OK", Left = 50, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
Button btnCancel = new Button() { Text = "Cancel", Left = 150, Width = 100, Top = txtTag.Top + txtTag.Height + 20 };
btnOK.Click += (s2, args2) =>
{
string newTag = txtTag.Text.Trim();
if (!string.IsNullOrEmpty(newTag))
{
// Update the tag in customTags list
int index = customTags.IndexOf(selectedTag);
customTags[index] = newTag;
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Update the menu item text
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item.Text == selectedTag)
{
item.Text = newTag;
break;
}
}
// Update selectedCustomTag if this tag was the selected one
if (selectedCustomTag == selectedTag)
{
selectedCustomTag = newTag;
}
// Update the listbox
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
// Close the edit form
editTagForm.DialogResult = DialogResult.OK;
editTagForm.Close();
}
else
{
MessageBox.Show("Tag cannot be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
};
btnCancel.Click += (s2, args2) =>
{
editTagForm.DialogResult = DialogResult.Cancel;
editTagForm.Close();
};
editTagForm.Controls.Add(lblTag);
editTagForm.Controls.Add(txtTag);
editTagForm.Controls.Add(btnOK);
editTagForm.Controls.Add(btnCancel);
editTagForm.AcceptButton = btnOK;
editTagForm.ShowDialog();
}
}
else
{
MessageBox.Show("Please select a tag to edit.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
};
btnDelete.Click += (s, args) =>
{
if (lstTags.SelectedItem != null)
{
string selectedTag = lstTags.SelectedItem.ToString();
var confirmResult = MessageBox.Show($"Are you sure you want to delete tag '{selectedTag}'?",
"Confirm Delete",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question);
if (confirmResult == DialogResult.Yes)
{
// Remove from customTags list
customTags.Remove(selectedTag);
// Save the custom tags to the file
SaveCustomTags(); // Eklendi
// Remove from menu items
ToolStripMenuItem itemToRemove = null;
foreach (ToolStripMenuItem item in customTagMenuItem.DropDownItems)
{
if (item.Text == selectedTag)
{
itemToRemove = item;
break;
}
}
if (itemToRemove != null)
{
customTagMenuItem.DropDownItems.Remove(itemToRemove);
}
// Clear selectedCustomTag if it was deleted
if (selectedCustomTag == selectedTag)
{
selectedCustomTag = null;
}
// Update the listbox
lstTags.DataSource = null;
lstTags.DataSource = new List<string>(customTags);
}
}
else
{
MessageBox.Show("Please select a tag to delete.", "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
};
btnClose.Click += (s, args) =>
{
manageTagsForm.Close();
};
manageTagsForm.Controls.Add(lstTags);
manageTagsForm.Controls.Add(btnEdit);
manageTagsForm.Controls.Add(btnDelete);
manageTagsForm.Controls.Add(btnClose);
manageTagsForm.ShowDialog();
}
}
// Custom Tags'i kaydetme metodu
private void SaveCustomTags()
{
try
{
string json = JsonConvert.SerializeObject(customTags);
File.WriteAllText(customTagsFilePath, json);
}
catch (Exception ex)
{
MessageBox.Show($"Error saving custom tags: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// Custom Tags'i yükleme metodu
private void LoadCustomTags()
{
if (File.Exists(customTagsFilePath))
{
try
{
string json = File.ReadAllText(customTagsFilePath);
customTags = JsonConvert.DeserializeObject<List<string>>(json);
// customTagMenuItem içindeki mevcut özel etiketleri temizleyelim (Add Tag ve Manage Tags hariç)
for (int i = customTagMenuItem.DropDownItems.Count - 1; i >= 0; i--)
{
var item = customTagMenuItem.DropDownItems[i];
if (item != addTagMenuItem && item != manageTagsMenuItem)
{
customTagMenuItem.DropDownItems.RemoveAt(i);
}
}
// customTags listesindeki etiketleri menüye ekleyelim
foreach (var tag in customTags)
{
AddCustomTagMenuItem(tag);
}
}
catch (Exception ex)
{
MessageBox.Show($"Error loading custom tags: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
private async void OnlineStatusTimer_Tick(object sender, EventArgs e)
{
bool currentlyOnline = CheckInternetConnection();
if (currentlyOnline && !isOnline)
{
// We were offline, now online
isOnline = true;
await ProcessOfflineChanges();
// Start real-time listener if not already started
if (!isListening)
{
RealTimeListener();
}
}
else if (!currentlyOnline && isOnline)
{
// We were online, now offline
isOnline = false;
}
}
private async Task ProcessOfflineChanges()
{
foreach (var change in offlineChanges)
{
switch (change.ChangeType)
{
case ChangeType.Add:
// Assume change.Flashcard.id already set
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.PutAsync(change.Flashcard);
break;
case ChangeType.Update:
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.PutAsync(change.Flashcard);
break;
case ChangeType.Delete:
await firebaseClient
.Child("flashcards")
.Child(change.Flashcard.id)
.DeleteAsync();
break;
}
}
offlineChanges.Clear();
}
}
/// <summary>
/// Represents a Flashcard item.
/// </summary>
public class FlashcardItem
{
public string id { get; set; }
public string category { get; set; }
public string title { get; set; }
public string content { get; set; }
public bool favorite { get; set; }
public bool pinned { get; set; }
public long timestamp { get; set; }
public string backgroundColor { get; set; }
}
public enum ChangeType { Add, Update, Delete }
public class FlashcardChange
{
public ChangeType ChangeType { get; set; }
public FlashcardItem Flashcard { get; set; }
}
}
```
**Explanation of Changes:**
- **Enabled `VirtualMode` in `DataGridView`:** Set `VirtualMode = true` in the initialization of `dataGridView` to enable virtual mode, which allows for more efficient handling of large data sets.
- **Implemented `CellValueNeeded` Event:** Added the `DataGridView_CellValueNeeded` method to supply the data to the `DataGridView` cells on demand. This method retrieves data from the `displayedFlashcards` list based on the row index.
- **Adjusted Data Binding:** Removed the direct data binding using `DataSource` and instead managed the displayed data manually using the `displayedFlashcards` list. Updated the `UpdateDataGridView` method accordingly.
- **Modified Event Handlers:** Updated event handlers like `DataGridView_SelectionChanged`, `DataGridView_CellContentClick`, `UpdateData`, and `DeleteData` to work with the virtual mode by referencing the `displayedFlashcards` list instead of relying on the `DataBoundItem`.
- **Performance Improvement:** By implementing virtual mode, the application now provides data to the `DataGridView` as needed, which significantly improves performance and responsiveness when working with large datasets.
This should resolve the interface freezing issues and enhance the application's fluidity without compromising its functionality. Let me know if you need any further assistance!