// ============================================ // 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; }