diff --git a/exercises/final_project/todo_cpp/todo.cpp b/exercises/final_project/todo_cpp/todo.cpp new file mode 100644 index 0000000..a5d1690 --- /dev/null +++ b/exercises/final_project/todo_cpp/todo.cpp @@ -0,0 +1,332 @@ +// ============================================ +// Final-Projekt: Todo-App in C++ +// Moderne C++17 Implementierung mit STL +// ============================================ + +#include +#include +#include +#include +#include +#include +#include +#include + +// ============================================ +// KLASSE: Todo +// ============================================ +class Todo { +private: + int id_; + std::string title_; + std::string description_; + bool completed_; + std::time_t created_at_; + +public: + // Konstruktor + Todo(int id, std::string title, std::string description) + : id_(id) + , title_(std::move(title)) + , description_(std::move(description)) + , completed_(false) + , created_at_(std::time(nullptr)) {} + + // Getter + int id() const { return id_; } + const std::string& title() const { return title_; } + const std::string& description() const { return description_; } + bool completed() const { return completed_; } + std::time_t created_at() const { return created_at_; } + + // Setter + void toggle_completed() { completed_ = !completed_; } + void set_title(const std::string& title) { title_ = title; } + void set_description(const std::string& desc) { description_ = desc; } + + // Formatierung für Ausgabe + std::string to_string() const { + std::stringstream ss; + ss << "#" << id_ << " [" << (completed_ ? "✓" : " ") << "] " << title_; + return ss.str(); + } + + // Serialisierung für Datei-Speicherung + std::string serialize() const { + std::stringstream ss; + ss << id_ << "|" << title_ << "|" << description_ << "|" + << (completed_ ? "1" : "0") << "|" << created_at_; + return ss.str(); + } + + static Todo deserialize(const std::string& line) { + std::stringstream ss(line); + std::string part; + std::vector parts; + + while (std::getline(ss, part, '|')) { + parts.push_back(part); + } + + Todo todo(std::stoi(parts[0]), parts[1], parts[2]); + if (parts[3] == "1") todo.toggle_completed(); + return todo; + } +}; + +// ============================================ +// KLASSE: TodoManager +// ============================================ +class TodoManager { +private: + std::vector todos_; + int next_id_ = 1; + const std::string filename_ = "todos_cpp.txt"; + +public: + // CRUD Operationen + void add(const std::string& title, const std::string& description) { + todos_.emplace_back(next_id_++, title, description); + std::cout << "✓ Todo hinzugefügt!\n"; + } + + void list() const { + if (todos_.empty()) { + std::cout << "Keine Todos vorhanden.\n"; + return; + } + + std::cout << "\n=== DEINE TODOS ===\n"; + for (const auto& todo : todos_) { + std::cout << todo.to_string() << "\n"; + if (!todo.description().empty()) { + std::cout << " " << todo.description() << "\n"; + } + } + std::cout << "\n"; + } + + void complete(int id) { + auto it = find_by_id(id); + if (it != todos_.end()) { + it->toggle_completed(); + std::cout << "✓ Todo #" << id << (it->completed() ? " abgeschlossen" : " wieder geöffnet") << "\n"; + } else { + std::cout << "✗ Todo #" << id << " nicht gefunden!\n"; + } + } + + void remove(int id) { + auto it = find_by_id(id); + if (it != todos_.end()) { + todos_.erase(it); + // IDs neu zuweisen + for (size_t i = 0; i < todos_.size(); ++i) { + // Note: In echtem Code würde man IDs nicht ändern + } + std::cout << "✓ Todo #" << id << " gelöscht!\n"; + } else { + std::cout << "✗ Todo #" << id << " nicht gefunden!\n"; + } + } + + void edit(int id, const std::string& new_title) { + auto it = find_by_id(id); + if (it != todos_.end()) { + it->set_title(new_title); + std::cout << "✓ Todo #" << id << " aktualisiert!\n"; + } else { + std::cout << "✗ Todo #" << id << " nicht gefunden!\n"; + } + } + + // Filter-Funktionen + void list_open() const { + std::cout << "\n=== OFFENE TODOS ===\n"; + for (const auto& todo : todos_) { + if (!todo.completed()) { + std::cout << todo.to_string() << "\n"; + } + } + std::cout << "\n"; + } + + void list_completed() const { + std::cout << "\n=== ABGESCHLOSSENE TODOS ===\n"; + for (const auto& todo : todos_) { + if (todo.completed()) { + std::cout << todo.to_string() << "\n"; + } + } + std::cout << "\n"; + } + + void search(const std::string& keyword) const { + std::cout << "\n=== SUCHERGEBNISSE ===\n"; + for (const auto& todo : todos_) { + if (todo.title().find(keyword) != std::string::npos || + todo.description().find(keyword) != std::string::npos) { + std::cout << todo.to_string() << "\n"; + } + } + std::cout << "\n"; + } + + // Datei-Operationen + void save() const { + std::ofstream file(filename_); + if (!file) { + std::cout << "✗ Fehler beim Speichern!\n"; + return; + } + + for (const auto& todo : todos_) { + file << todo.serialize() << "\n"; + } + + std::cout << "✓ " << todos_.size() << " Todos gespeichert!\n"; + } + + void load() { + std::ifstream file(filename_); + if (!file) { + std::cout << "Keine vorhandenen Daten. Starte neu...\n"; + return; + } + + todos_.clear(); + std::string line; + while (std::getline(file, line)) { + if (!line.empty()) { + todos_.push_back(Todo::deserialize(line)); + } + } + + // next_id aktualisieren + if (!todos_.empty()) { + next_id_ = std::max_element(todos_.begin(), todos_.end(), + [](const Todo& a, const Todo& b) { return a.id() < b.id(); })->id() + 1; + } + + std::cout << "✓ " << todos_.size() << " Todos geladen!\n"; + } + +private: + std::vector::iterator find_by_id(int id) { + return std::find_if(todos_.begin(), todos_.end(), + [id](const Todo& t) { return t.id() == id; }); + } +}; + +// ============================================ +// UI-FUNKTIONEN +// ============================================ +void print_menu() { + std::cout << "\n=== TODO-APP (C++ Edition) ===\n"; + std::cout << "1. Todo hinzufügen\n"; + std::cout << "2. Alle Todos anzeigen\n"; + std::cout << "3. Offene Todos anzeigen\n"; + std::cout << "4. Abgeschlossene Todos anzeigen\n"; + std::cout << "5. Todo abschließen/öffnen\n"; + std::cout << "6. Todo bearbeiten\n"; + std::cout << "7. Todo löschen\n"; + std::cout << "8. Suchen\n"; + std::cout << "9. Speichern\n"; + std::cout << "0. Beenden\n"; + std::cout << "Wahl: "; +} + +// ============================================ +// HAUPTPROGRAMM +// ============================================ +int main() { + TodoManager manager; + manager.load(); + + std::cout << "\n📝 Willkommen zur Todo-App (C++17)!\n"; + + int choice; + std::string title, description, keyword; + int id; + + while (true) { + print_menu(); + + if (!(std::cin >> choice)) { + std::cin.clear(); + std::cin.ignore(10000, '\n'); + std::cout << "Ungültige Eingabe!\n"; + continue; + } + std::cin.ignore(10000, '\n'); // Rest der Zeile ignorieren + + switch (choice) { + case 1: + std::cout << "Titel: "; + std::getline(std::cin, title); + std::cout << "Beschreibung: "; + std::getline(std::cin, description); + manager.add(title, description); + break; + + case 2: + manager.list(); + break; + + case 3: + manager.list_open(); + break; + + case 4: + manager.list_completed(); + break; + + case 5: + std::cout << "Todo-ID: "; + std::cin >> id; + manager.complete(id); + break; + + case 6: + std::cout << "Todo-ID: "; + std::cin >> id; + std::cin.ignore(10000, '\n'); + std::cout << "Neuer Titel: "; + std::getline(std::cin, title); + manager.edit(id, title); + break; + + case 7: + std::cout << "Todo-ID: "; + std::cin >> id; + manager.remove(id); + break; + + case 8: + std::cout << "Suchbegriff: "; + std::getline(std::cin, keyword); + manager.search(keyword); + break; + + case 9: + manager.save(); + break; + + case 0: { + std::cout << "Speichern vor Beenden? (j/n): "; + char save_choice; + std::cin >> save_choice; + if (save_choice == 'j' || save_choice == 'J') { + manager.save(); + } + std::cout << "Auf Wiedersehen!\n"; + return 0; + } + + default: + std::cout << "Ungültige Wahl!\n"; + } + } + + return 0; +}