Kitlist
A list manager for maintaining kit lists
Loading...
Searching...
No Matches
kitlistgui.cpp
Go to the documentation of this file.
1/*
2
3 This file is part of Kitlist, a program to maintain a simple list
4 of items and assign items to one or more categories.
5
6 Copyright (C) 2008-2025 Frank Dean
7
8 Kitlist is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 Kitlist is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Kitlist. If not, see <http://www.gnu.org/licenses/>.
20
21*/
22#include "config.h"
23#include "kitlistgui.hpp"
24#include "kitparser.hpp"
25#ifdef HAVE_FINALCUT
26#include "kitlist_finalcut.hpp"
27#endif
28#ifdef HAVE_FTXUI
29#include "kitlist_ftxui.hpp"
30#endif
31#include "wx/aboutdlg.h"
32#include "wx/app.h"
33#include "wx/cmdline.h"
34#include "wx/config.h"
35#include "wx/dcmirror.h"
36#include "wx/filefn.h"
37#include "wx/filename.h"
38#include <memory>
39#include <vector>
40
41using namespace std;
42
43// ----------------------------------------------------------------------------
44// resources
45// ----------------------------------------------------------------------------
46
47#ifndef wxHAS_IMAGES_IN_RESOURCES
48 #include "../kitlist.xpm"
49#endif
50
51// ----------------------------------------------------------------------------
52// MySplitterWindow
53// ----------------------------------------------------------------------------
54
56 EVT_SPLITTER_SASH_POS_CHANGED(wxID_ANY, MySplitterWindow::OnPositionChanged)
57wxEND_EVENT_TABLE()
58
59void MySplitterWindow::OnPositionChanged(wxSplitterEvent& WXUNUSED(event))
60{
61#ifdef __linux__
62 // Workaround where the sash is positioned incorrectly on startup when running on Linux
63 if (ToDIP(frame->get_right_panel_width()).GetWidth() < 1)
64 SetSashPosition(MyFrame::VSPLIT_DIST);
65#endif
66}
67
68// ----------------------------------------------------------------------------
69// MyFrame
70// ----------------------------------------------------------------------------
71
72const int MyFrame::VSPLIT_DIST = 200;
73const int MyFrame::MIN_PANE_SIZE = 1;
74const int MyFrame::ITEM_NAME_COL = 0;
75const int MyFrame::ITEM_CHECKED_COL = 1;
76const int MyFrame::CATEGORY_NAME_COL = 0;
77
78enum
79{
80 QUIT = 1,
114};
115
117 EVT_SHOW(MyFrame::OnShow)
118 EVT_MENU(wxID_ABOUT, MyFrame::OnAbout)
119 EVT_MENU(wxID_FILE1, MyFrame::OnFileHistoryMenuItem)
120 EVT_MENU(wxID_FILE2, MyFrame::OnFileHistoryMenuItem)
121 EVT_MENU(wxID_FILE3, MyFrame::OnFileHistoryMenuItem)
122 EVT_MENU(wxID_FILE4, MyFrame::OnFileHistoryMenuItem)
123 EVT_MENU(wxID_FILE5, MyFrame::OnFileHistoryMenuItem)
124 EVT_MENU(wxID_FILE6, MyFrame::OnFileHistoryMenuItem)
125 EVT_MENU(wxID_FILE7, MyFrame::OnFileHistoryMenuItem)
126 EVT_MENU(wxID_FILE8, MyFrame::OnFileHistoryMenuItem)
127 EVT_MENU(wxID_FILE9, MyFrame::OnFileHistoryMenuItem)
129 EVT_MENU(OPEN_FILE, MyFrame::OnOpen)
130 EVT_MENU(SAVE, MyFrame::OnSave)
131 EVT_MENU(SAVE_AS, MyFrame::OnSaveAs)
132 EVT_MENU(QUIT, MyFrame::OnQuit)
133 EVT_MENU(wxID_PRINT, MyFrame::OnPrint)
134 EVT_MENU(wxID_PREVIEW, MyFrame::OnPrintPreview)
135 EVT_DATAVIEW_SELECTION_CHANGED(SELECT_CATEGORY, MyFrame::OnSelectCategory)
136 EVT_DATAVIEW_SELECTION_CHANGED(SELECT_ITEM, MyFrame::OnSelectItem)
137 EVT_DATAVIEW_ITEM_VALUE_CHANGED(CATEGORY_ITEM_CHANGE, MyFrame::OnCategoryListValueChanged)
138 EVT_DATAVIEW_ITEM_VALUE_CHANGED(ITEM_CHANGE, MyFrame::OnItemListValueChanged)
139 EVT_DATAVIEW_ITEM_START_EDITING(CATEGORY_ITEM_START_EDITING, MyFrame::OnCategoryStartEditing)
140 EVT_DATAVIEW_ITEM_CONTEXT_MENU(CATEGORY_CONTEXT_MENU, MyFrame::OnCategoryContextMenu)
141 EVT_DATAVIEW_ITEM_CONTEXT_MENU(ITEM_CONTEXT_MENU, MyFrame::OnItemContextMenu)
160wxEND_EVENT_TABLE()
161
162MyFrame::MyFrame(KitListGui* app, const wxPoint& pos)
163 : wxFrame(nullptr, wxID_ANY, "Kitlist", pos),
164 rich_text_printing(),
165 gui_app(app),
166 category_list_ctrl(),
167 file_menu(),
168 edit_menu(),
169 last_error(nullptr) {
170
171 wxLOG_COMPONENT = "KitList/MyFrame";
174 SetIcon(wxICON(kitlist));
175 CreateStatusBar(2);
176
177 file_history_menu = new wxMenu();
178 file_history = new wxFileHistory();
179 file_history->UseMenu(file_history_menu);
180
181 file_menu = new wxMenu;
182 file_menu->Append(NEW_FILE, L"&New\tCtrl-N", "Create a new kitlist");
183 file_menu->Append(OPEN_FILE, L"&Open…\tCtrl-O", "Open…");
184 file_history_menu_item = file_menu->AppendSubMenu(file_history_menu, L"Open &Recent…");
185 file_menu->AppendSeparator();
186 file_menu->Append(SAVE, "&Save\tCtrl-S", "Save");
187 file_menu->Append(SAVE_AS, L"Save &As…\tShift-Ctrl-S", L"Save As…");
188 file_menu->AppendSeparator();
189 file_menu->Append(wxID_PRINT, "&Print...\tCtrl+P", "Show \"Print\" dialog");
190 file_menu->Append(wxID_PREVIEW, "Pre&view\tAlt-Shift-Ctrl+V", "Show print preview");
191 file_menu->AppendSeparator();
192 file_menu->Append(QUIT, "&Quit\tCtrl-Q", "Quit");
193
194 auto config = wxConfig::Get();
195 auto old_path = config->GetPath();
196 config->SetPath("/FileHistory");
197 file_history->Load(*config);
198 config->SetPath(old_path);
199
200 file_menu->Enable(file_history_menu_item->GetId(), file_history->GetCount() > 0);
201 update_file_menu(false);
202
203 edit_menu = new wxMenu;
204 edit_menu->Append(ADD_ITEM, L"New &Item…\tShift-Ctrl-I", L"Create new item");
205 edit_menu->Append(ADD_CATEGORY, L"New &Category…\tShift-Ctrl-N", L"Create new category");
206 edit_menu->AppendSeparator();
207 edit_menu->Append(SELECT_ALL_ITEMS, "Select &All\tCtrl-A", "Select all items");
208 edit_menu->Append(UNSELECT_ALL_ITEMS, "&Unse&lect All\tCtrl-U", "Unselect all items");
209 edit_menu->AppendSeparator();
210 edit_menu->Append(CHECK_SELECTED, "Chec&k Selected\tAlt-Ctrl-K", "Check selected items");
211 edit_menu->Append(UNCHECK_SELECTED, "Uncheck &Selected\tAlt-Ctrl-U", "Uncheck selected items");
212 edit_menu->Append(TOGGLE_SELECTED, "&Toggle Selected\tAlt-Ctrl-T", "Toggle selected items");
213 edit_menu->AppendSeparator();
214 edit_menu->Append(COPY_SELECTED_ITEMS_TO_CATEGORIES, L"Cop&y Selected Items…\tShift-Ctrl-C", "Copy selected items to one or more categories");
215 edit_menu->Append(REMOVE_SELECTED_ITEMS, "&Remove Selected Items\tShift-Ctrl-R", "Remove selected items from the current category");
216 edit_menu->AppendSeparator();
217 edit_menu->Append(REMOVE_CHECKED_ITEMS, "&Remove Checked Items\tShift-Ctrl-H", "Remove checked items from the current category");
218 edit_menu->Append(DELETE_CHECKED_ITEMS, "&Delete Checked Items\tShift-Ctrl-H", "Delete checked items from the current category");
219 edit_menu->AppendSeparator();
220 edit_menu->Append(CHECK_ALL, "Ch&eck all\tAlt-Shift-Ctrl-K", "Check all items in list");
221 edit_menu->Append(UNCHECK_ALL, "Unc&heck all\tAlt-Shift-Ctrl-U", "Uncheck all items in list");
222 edit_menu->Append(TOGGLE_ALL, "To&ggle all\tAlt-Shift-Ctrl-T", "Toggle all items in the list");
223 edit_menu->AppendSeparator();
224 edit_menu->Append(COPY_CHECKED_TO_CATEGORIES, L"Copy Checked Ite&ms…\tAlt-Ctrl-c", "Copy checked items to one or more categories");
225
226 edit_menu->Enable(CHECK_SELECTED, false);
227 edit_menu->Enable(UNCHECK_SELECTED, false);
228 edit_menu->Enable(TOGGLE_SELECTED, false);
229 edit_menu->Enable(REMOVE_SELECTED_ITEMS, false);
230 edit_menu->Enable(REMOVE_CHECKED_ITEMS, false);
231 edit_menu->Enable(
233 gui_app->count_filter_items_for_current_selected_category() > 0);
234 edit_menu->Enable(COPY_SELECTED_ITEMS_TO_CATEGORIES, false);
235
236 wxMenu* view_menu = new wxMenu;
237 view_menu->Append(SHOW_ALL_EVENT, "Show &All\tShift-Ctrl-A", "Show all", wxITEM_RADIO);
238 view_menu->Append(SHOW_CHECKED_EVENT, "Show &Checked\tShift-Ctrl-K", "Show checked", wxITEM_RADIO);
239 view_menu->Append(SHOW_UNCHECKED_EVENT, "Show &Unchecked\tShift-Ctrl-U", "Show unchecked", wxITEM_RADIO);
240
241 wxMenu* help_menu = new wxMenu;
242 help_menu->Append(wxID_ABOUT, "&About", "About Kitlist");
243
244 wxMenuBar* menuBar = new wxMenuBar;
245 menuBar->Append(file_menu, "&File");
246 menuBar->Append(edit_menu, "&Edit");
247 menuBar->Append(view_menu, "&View");
248 menuBar->Append(help_menu, "&Help");
249
250 SetMenuBar(menuBar);
251
252 split_window = new MySplitterWindow(this);
253
254 split_window->SetSize(GetClientSize());
255 split_window->SetSashGravity(0.25);
256 split_window->SetMinimumPaneSize(MIN_PANE_SIZE);
257
258 // If the filename is set, we were given it on the command line
259 auto filename = gui_app->get_filename();
260
261 // If not set on the command line, see if there's one in the file history
262 if (filename.empty() && file_history->GetCount() > 0) {
263 const wxString last_file = file_history->GetHistoryFile(0);
264 if (!last_file.empty() && wxFileExists(last_file)) {
265 // wxLogDebug("Last file loaded: \"%s\"", last_file);
266 filename = last_file.ToStdString();
267 gui_app->set_filename(filename);
268 }
269 }
270
271 if (!filename.empty()) {
272 try {
273 wxLogInfo("Loading \"%s\"", filename);
274 load_file(filename);
275 file_history->AddFileToHistory(filename);
276 file_menu->Enable(file_history_menu_item->GetId(), true);
277 } catch (const KitListGui::file_not_found& e) {
278 wxLogError("%s", e.what());
279 last_error = std::make_unique<KitListGui::file_not_found>(e);
280 } catch (const KitParser::parse_exception& e) {
281 wxLogError("%s", e.what());
282 last_error = std::make_unique<KitParser::parse_exception>(e);
283 }
284 }
285
286 auto category_list_sizer = new wxBoxSizer(wxVERTICAL);
287 left_panel = new wxPanel(split_window, wxID_ANY);
288 category_list_ctrl = new wxDataViewListCtrl(left_panel, wxID_ANY);
289 category_list_ctrl->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &MyFrame::OnCategoryListValueChanged, this);
290 category_list_ctrl->Bind(wxEVT_DATAVIEW_ITEM_START_EDITING, &MyFrame::OnCategoryStartEditing, this);
291 category_list_ctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MyFrame::OnSelectCategory, this);
292 category_list_ctrl->AppendTextColumn(
293 "Categories",
294 wxDATAVIEW_CELL_EDITABLE,
295 -1,
296 wxALIGN_LEFT,
297 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE);
298 populate_category_list();
299 category_list_sizer->Add(category_list_ctrl, 1, wxGROW);
300 left_panel->SetSizerAndFit(category_list_sizer);
301
302 auto item_list_sizer = new wxBoxSizer(wxVERTICAL);
303 right_panel = new wxPanel(split_window, wxID_ANY);
304 item_list_ctrl = new wxDataViewListCtrl(right_panel, wxID_ANY,
305 wxDefaultPosition,
306 wxDefaultSize,
307 wxDV_ROW_LINES | wxDV_MULTIPLE);
308 item_list_ctrl->Bind(wxEVT_DATAVIEW_SELECTION_CHANGED, &MyFrame::OnSelectItem, this);
309 item_list_ctrl->Bind(wxEVT_DATAVIEW_ITEM_VALUE_CHANGED, &MyFrame::OnItemListValueChanged, this);
310 category_list_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyFrame::OnCategoryContextMenu, this);
311 item_list_ctrl->Bind(wxEVT_DATAVIEW_ITEM_CONTEXT_MENU, &MyFrame::OnItemContextMenu, this);
312 // Only the first column has text search enabled on platforms using GTK.
313 //
314 // If we place the checked column first, search is enabled but doesn't do
315 // anything useful. If the user subsequently moves the checked column to be
316 // the first, text search on the name column still works.
317 //
318 // https://github.com/wxWidgets/wxWidgets/issues/23779
319 wxDataViewColumn* name_view_column = item_list_ctrl->AppendTextColumn(
320 "Item",
321 wxDATAVIEW_CELL_EDITABLE,
322 -1,
323 wxALIGN_LEFT,
324 wxDATAVIEW_COL_RESIZABLE |
325 wxDATAVIEW_COL_REORDERABLE |
326 wxDATAVIEW_COL_SORTABLE);
327 wxDataViewColumn* checked_view_column = item_list_ctrl->AppendToggleColumn(
328 "Checked",
329 wxDATAVIEW_CELL_ACTIVATABLE,
330 -1,
331 wxALIGN_LEFT,
332 wxDATAVIEW_COL_RESIZABLE | wxDATAVIEW_COL_REORDERABLE | wxDATAVIEW_COL_SORTABLE);
333
334 checked_view_column->SetMinWidth(40);
335 checked_view_column->SetWidth(60);
336 name_view_column->SetMinWidth(100);
337 name_view_column->SetWidth(270);
338 populate_item_list();
339 item_list_sizer->Add(item_list_ctrl, 1, wxGROW);
340 right_panel->SetSizerAndFit(item_list_sizer);
341
342 // Part of the workaround where the sash is positioned incorrectly when running on Linux
343 // #ifdef __linux__
344 // left_panel->SetMinSize(FromDIP(wxSize(vsplit_dist + 1, 0)));
345 // #endif
346 // you can also try -100
347 split_window->SplitVertically(left_panel, right_panel, FromDIP(VSPLIT_DIST));
348
349 old_path = config->GetPath();
350 config->SetPath("/MainFrame");
351
352 // restore frame position and size
353 int x = config->Read("x", 50),
354 y = config->Read("y", 50),
355 w = config->Read("w", 640),
356 h = config->Read("h", 480);
357 Move(x, y);
358 SetClientSize(w, h);
359 config->SetPath(old_path);
360 gui_app->initialization_complete();
361 selected_item_changed();
362}
363
365{
366 wxConfigBase* config = wxConfig::Get();
367 if (config == nullptr)
368 return;
369
370 // save the frame position
371 int x, y, w, h;
372 GetClientSize(&w, &h);
373 GetPosition(&x, &y);
374 config->Write("/MainFrame/x", (long) x);
375 config->Write("/MainFrame/y", (long) y);
376 config->Write("/MainFrame/w", (long) w);
377 config->Write("/MainFrame/h", (long) h);
378}
379
380void MyFrame::OnCategoryListValueChanged(wxDataViewEvent& event)
381{
382 const wxDataViewItem anItem = event.GetItem();
383 const int row = category_list_ctrl->ItemToRow(anItem);
384 if (row != wxNOT_FOUND && row > 0) {
385 const wxUIntPtr item_data = category_list_ctrl->GetItemData(anItem);
386 const int32_t cat_id = item_data;
387 wxVariant value;
388 category_list_ctrl->GetValue(value, row, CATEGORY_NAME_COL);
389 wxString s(value);
390 s.Trim();
391 gui_app->set_category_name(cat_id, s.ToStdString());
392 }
393}
394
395void MyFrame::OnItemListValueChanged(wxDataViewEvent& event)
396{
397 const wxDataViewItem anItem = event.GetItem();
398 const int row = item_list_ctrl->ItemToRow(anItem);
399 if (row != wxNOT_FOUND) {
400 const wxUIntPtr item_data = item_list_ctrl->GetItemData(anItem);
401 const int32_t id = item_data;
402 // wxLogDebug("Searching for item %d", i);
403 shared_ptr<Item> item = gui_app->get_item(id);
404 if (item) {
405 // NOTE: if the columns have been re-ordered, the original column number
406 // no longer reflects what is reported in the event. The event reports
407 // the new position the column ID but also returns the wxDataViewItem
408 // based on the original column order not the actual one associated with
409 // the event.
410 //
411 // So, we retrieve the values of both columns and check to see if they
412 // have changed.
413 // wxLogDebug("item %d: Row %d, column %d", i, row, event.GetColumn());
414 wxVariant name_value, checked_value;
415 item_list_ctrl->GetValue(name_value, row, ITEM_NAME_COL);
416 item_list_ctrl->GetValue(checked_value, row, ITEM_CHECKED_COL);
417 const bool b = checked_value;
418 if (b != item->is_checked()) {
419 gui_app->set_item_checked(id, b);
420 // wxLogDebug("Item %s", b ? "checked" : "unchecked");
421 }
422 wxString s(name_value);
423 s.Trim();
424 gui_app->set_item_name(id, s.ToStdString());
425 } else {
426 wxLogError("Failed to find item for row %d", row);
427 }
428 } else {
429 wxLogError("Failed to find row for event");
430 }
431}
432
433void MyFrame::OnCheckSelected(wxCommandEvent& WXUNUSED(event))
434{
437}
438
439void MyFrame::OnUncheckSelected(wxCommandEvent& WXUNUSED(event))
440{
443}
444
445void MyFrame::OnToggleSelected(wxCommandEvent& WXUNUSED(event))
446{
449}
450
452{
453 wxDataViewItemArray view_items;
454 const unsigned long long count = item_list_ctrl->GetSelections(view_items);
455 if (count == 0)
456 return;
457 for (const auto& view_item : view_items) {
458 gui_app->change_checked_state(
459 item_list_ctrl->GetItemData(view_item), action);
460 }
462 update_file_menu(gui_app->is_dirty());
463}
464
465void MyFrame::OnRemoveSelectedItems(wxCommandEvent& WXUNUSED(event))
466{
467 const auto cat_id = gui_app->get_selected_category();
468 assert(cat_id != Model::no_category);
469 if (cat_id == Model::no_category)
470 return;
471
472 wxDataViewItemArray view_items;
473 const auto count = item_list_ctrl->GetSelections(view_items);
474 if (count == 0)
475 return;
476 vector<shared_ptr<Item>> selected_items;
477 for (const auto& view_item : view_items) {
478 const int32_t id = item_list_ctrl->GetItemData(view_item);
479 auto item = gui_app->get_item(id);
480 if (item) {
481 selected_items.push_back(item);
482 }
483 }
484 gui_app->remove_items(selected_items);
487 update_file_menu(gui_app->is_dirty());
488}
489
490void MyFrame::OnRemoveCheckedItems(wxCommandEvent& WXUNUSED(event))
491{
492 gui_app->remove_all_current_checked_items();
495 update_file_menu(gui_app->is_dirty());
496}
497
498void MyFrame::OnDeleteCheckedItems(wxCommandEvent& WXUNUSED(event))
499{
500 gui_app->delete_all_current_checked_items();
503 update_file_menu(gui_app->is_dirty());
504}
505
506void MyFrame::CopyCheckedToCategories(wxCommandEvent& WXUNUSED(event))
507{
508 const auto items = gui_app->get_all_items_for_current_selected_category();
509 vector<shared_ptr<Item>> selected_items;
510 for (const auto& item : items)
511 if (item->is_checked())
512 selected_items.push_back(item);
513 select_and_copy_to_categories(selected_items);
514}
515
516void MyFrame::OnCopySelectedItemsToCategories(wxCommandEvent& WXUNUSED(event))
517{
518 wxDataViewItemArray view_items;
519 const auto count = item_list_ctrl->GetSelections(view_items);
520 if (count == 0)
521 return;
522 vector<shared_ptr<Item>> selected_items;
523 for (const auto& view_item : view_items) {
524 const int32_t id = item_list_ctrl->GetItemData(view_item);
525 auto item = gui_app->get_item(id);
526 if (item) {
527 selected_items.push_back(item);
528 }
529 }
530 select_and_copy_to_categories(selected_items);
531}
532
533void MyFrame::select_and_copy_to_categories(vector<shared_ptr<Item>> selected_items)
534{
535 wxArrayString s_cat;
536 auto categories = gui_app->get_categories();
537 for (const auto& c : categories)
538 s_cat.Add(wxString::FromUTF8(c->get_name()));
539 wxMultiChoiceDialog dlg(this, "Select target categories", "Categories", s_cat);
540 if (dlg.ShowModal() == wxID_CANCEL)
541 return;
542 wxArrayInt selected = dlg.GetSelections();
543 vector<shared_ptr<Category>> selected_categories;
544 for (unsigned int i = 0; i < selected.size(); i++) {
545 const unsigned int idx = selected[i];
546 if (idx >= 0 && idx < categories.size())
547 selected_categories.push_back(categories[idx]);
548 }
549 if (!selected_categories.empty())
550 gui_app->copy_items_to_categories(selected_items, selected_categories);
551 update_file_menu(gui_app->is_dirty());
552}
553
554void MyFrame::OnCategoryContextMenu(wxDataViewEvent& event)
555{
556 const wxDataViewItem anItem = event.GetItem();
557 if (!anItem.IsOk())
558 return;
559 const int row = category_list_ctrl->ItemToRow(anItem);
560 if (row == wxNOT_FOUND)
561 return;
562 const wxUIntPtr item_data = category_list_ctrl->GetItemData(anItem);
563 const int32_t id = item_data;
564 shared_ptr<Category> category = gui_app->get_category(id);
565 if (!category)
566 return;
567
568 wxMenu menu;
569 menu.Append(DELETE_CATEGORY, "&Delete category");
570 int result = GetPopupMenuSelectionFromUser(menu);
571 if (result == wxID_NONE)
572 return;
573 switch (result) {
574 case DELETE_CATEGORY:
575 delete_category(id);
576 break;
577 }
578 update_file_menu(gui_app->is_dirty());
579}
580
581void MyFrame::OnItemContextMenu(wxDataViewEvent& event)
582{
583 const wxDataViewItem anItem = event.GetItem();
584 if (!anItem.IsOk())
585 return;
586 const int row = item_list_ctrl->ItemToRow(anItem);
587 if (row == wxNOT_FOUND)
588 return;
589 const wxUIntPtr item_data = item_list_ctrl->GetItemData(anItem);
590 const int32_t i = item_data;
591 // wxLogDebug("Searching for item %d", i);
592 shared_ptr<Item> item = gui_app->get_item(i);
593 auto selected_category = gui_app->get_selected_category();
594 if (item) {
595 wxMenu menu;
596 if (selected_category < 0)
597 menu.Append(DELETE_ITEM, "&Delete item");
598 else
599 menu.Append(REMOVE_ITEM, "&Remove item");
600
601 menu.Append(COPY_ITEM_TO_CATEGORIES, "&Copy item");
602 int result = GetPopupMenuSelectionFromUser(menu);
603 if (result == wxID_NONE)
604 return;
605 switch (result) {
606 case DELETE_ITEM:
607 delete_item(i);
608 break;
609 case REMOVE_ITEM:
610 remove_item(i);
611 break;
613 vector<shared_ptr<Item>> items;
614 items.push_back(item);
616 break;
617 }
618 }
619}
620
621void MyFrame::OnSelectCategory(wxDataViewEvent& event)
622{
623 const wxDataViewItem anItem = event.GetItem();
624 const int row = category_list_ctrl->ItemToRow(anItem);
625 if (row == 0) {
626 gui_app->select_category(Model::no_category);
627 } else if (row != wxNOT_FOUND && row > 0) {
628 const wxUIntPtr category_data = category_list_ctrl->GetItemData(anItem);
629 const int32_t cat_id = category_data;
630 const auto found_category = gui_app->get_category(cat_id);
631 if (found_category)
632 gui_app->select_category(cat_id);
633 else
634 wxLogError("Failed to find category with ID %d", cat_id);
635 // } else {
636 // wxLogDebug("Row not found, not selecting a category");
637 }
639}
640
641void MyFrame::OnSelectItem(wxDataViewEvent& WXUNUSED(event))
642{
644}
645
650
651unsigned MyFrame::populate_category_list(int32_t select_category)
652{
653 unsigned retval = 0;
654 category_list_ctrl->DeleteAllItems();
655 auto categories = gui_app->get_categories();
656 wxVector<wxVariant> data;
657 data.push_back(wxVariant(gui_app->all_items_text));
658 category_list_ctrl->AppendItem(data, wxUIntPtr(CATEGORY_NAME_COL));
659 data.clear();
660 unsigned row = 0;
661 for (const shared_ptr<Category>& cat : categories) {
662 data.push_back(wxVariant(wxString::FromUTF8(cat->get_name())));
663 wxUIntPtr client_data = cat->get_id();
664 row++;
665 if (select_category == cat->get_id())
666 retval = row;
667 category_list_ctrl->AppendItem(data, client_data);
668 data.clear();
669 }
670 return retval;
671}
672
674{
675 item_list_ctrl->DeleteAllItems();
676 const auto items = gui_app->filter_items_for_current_selected_category();
677 wxVector<wxVariant> data;
678 for (const shared_ptr<Item>& item : items) {
679 wxUIntPtr client_data = item->get_id();
680 data.push_back(wxVariant(wxString::FromUTF8(item->get_name())));
681 data.push_back(wxVariant(item->is_checked()));
682 item_list_ctrl->AppendItem(data, client_data);
683 data.clear();
684 }
686}
687
689 auto config = wxConfig::Get();
690 const auto old_path = config->GetPath();
691 config->SetPath("/reports/item-list");
692 const auto title = config->Read("title", "Kitlist");
693 config->SetPath(old_path);
694 rich_text_printing = make_unique<wxRichTextPrinting>(title, this);
695 rich_text_printing->SetHeaderText("@TITLE@", wxRICHTEXT_PAGE_ALL, wxRICHTEXT_PAGE_CENTRE);
696 rich_text_printing->SetFooterText("- @PAGENUM@ / @PAGESCNT@ -", wxRICHTEXT_PAGE_ALL, wxRICHTEXT_PAGE_CENTRE);
697 rich_text_printing->SetParentWindow(this);
698}
699
700void MyFrame::OnPrint(wxCommandEvent& WXUNUSED(event))
701{
704 wxRichTextBuffer buf;
705 FillBuffer(buf);
706 rich_text_printing->PrintBuffer(buf);
707}
708
709void MyFrame::OnPrintPreview(wxCommandEvent& WXUNUSED(event))
710{
713 wxRichTextBuffer buf;
714 FillBuffer(buf);
715 rich_text_printing->PreviewBuffer(buf);
716}
717
718void MyFrame::FillBuffer(wxRichTextBuffer& buf) const
719{
720 const auto items = gui_app->filter_items_for_current_selected_category();
721 buf.BeginParagraphSpacing(10, 10);
722 for (const shared_ptr<Item>& item : items)
723 buf.AddParagraph(wxString::FromUTF8(item->get_name()));
724}
725
726void KitListGui::category_entry_changed(const shared_ptr<Category> category)
727{
728 if (my_frame) {
729 if (category) {
730 const auto row = my_frame->populate_category_list(category->get_id());
731 my_frame->category_list_ctrl->SelectRow(row);
732 } else {
733 my_frame->populate_category_list();
734 }
735 }
736}
737
738void KitListGui::item_entry_changed(const shared_ptr<Item> WXUNUSED(item))
739{
740 if (my_frame)
741 my_frame->populate_item_list();
742}
743
744void KitListGui::load_file(const std::string& filename)
745{
746 if (!wxFileExists(filename)) {
747 throw file_not_found("File not found: " + filename);
748 }
750}
751
753{
754 my_frame->update_edit_menu();
755 my_frame->update_file_menu(is_dirty);
756}
757
758void KitListGui::OnInitCmdLine(wxCmdLineParser& parser)
759{
760 wxApp::OnInitCmdLine(parser);
761 // parser.AddUsageText("Usage:\n kitlist [FILENAME]\n\n");
762 // parser.AddSwitch("h", "help", "show this help, then exit");
763 // parser.AddSwitch(nullptr, "verbose", "generate verbose log messages");
764 parser.AddSwitch("v", "version", "shows the application's version and exits");
765 parser.AddParam("FILENAME", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL);
766 #ifdef HAVE_FINALCUT
767 parser.AddSwitch("t", "finalcut", "runs the application with FINAL CUT Text-based User Interface");
768 #endif
769 #ifdef HAVE_FTXUI
770 parser.AddSwitch("x", "ftxui", "runs the application with FTXUI Text-based User Interface");
771 #endif
772}
773
774bool KitListGui::OnCmdLineParsed(wxCmdLineParser& parser)
775{
776
777 if (parser.GetParamCount() > 0) {
778 auto filename = parser.GetParam(0);
779 // If the filename is set, we will load the file later
780 set_filename(filename.ToStdString());
781 }
782 // Leave log level at default unless release build
783#ifdef NDEBUG
784 if (parser.Found("verbose")) {
785 wxLog::SetLogLevel(wxLOG_Debug);
786 } else {
787 wxLog::SetLogLevel(wxLOG_Message);
788 }
789#endif // NDEBUG
790 if (parser.Found("version")) {
791 std::cout << PACKAGE_NAME << " version " << PACKAGE_VERSION << std::endl;
792 return false;
793 }
794#ifdef HAVE_FINALCUT
795 if (parser.Found("finalcut")) {
797 finalcut.run(argc, argv, get_filename());
798 return false;
799 }
800#endif // HAVE_FINALCUT
801#ifdef HAVE_FTXUI
802 if (parser.Found("ftxui")) {
804 string filename = get_filename();
805 if (filename.empty()) {
806 wxLogError("Specify an existing kitlist file when using the FTXUI interface.");
807 return false;
808 }
809 ftxui_app.run(filename);
810 return false;
811 }
812#endif // HAVE_FTXUI
813 return wxApp::OnCmdLineParsed(parser);
814}
815
817{
818 if (!wxApp::OnInit())
819 return false;
820
821 wxLOG_COMPONENT = "KitList";
822 // Fails to compile on macOS aarch64 with GCC
823#ifdef __clang__
824 wxLog* logger = new wxLogStream(&cout);
825 wxLog::SetActiveTarget(logger);
826#else
827#ifndef __APPLE__
828 wxLog* logger = new wxLogStream(&cout);
829 wxLog::SetActiveTarget(logger);
830#else
831 cerr << "Disabling logging as compiled with GCC on Apple aarch64\n";
832 wxLog::EnableLogging(false);
833#endif
834#endif
835
836#ifndef NDEBUG // Is a release build
837 wxLogWarning("Assertions are enabled. Configure with --disable-assertions to disable them.");
838#endif
839 SetVendorName("uk.co.fdsd.kitlist");
840 SetAppName("kitlist-2");
841 auto config = new wxConfig(GetAppName(), GetVendorName(), wxString{}, wxString{}, wxCONFIG_USE_LOCAL_FILE);
842 // Cause reading with defaults from config file to update the file with the default.
843 config->SetRecordDefaults();
844 wxConfig::Set(config);
845
846 my_frame = new MyFrame(this, wxPoint(100, 100));
847 my_frame->Show();
848 return true;
849}
850
852{
853 return wxApp::OnExit();
854}
855
856void MyFrame::OnShow(wxShowEvent& event)
857{
858 if (last_error && event.IsShown()) {
859 CallAfter([&]{
860 wxMessageBox(last_error->what(), "Error", wxICON_ERROR);
861 last_error.reset();
862 });
863 }
864}
865
866// menu command handlers
867
868void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
869{
870 wxAboutDialogInfo aboutInfo;
871 aboutInfo.SetName("Kitlist");
872 aboutInfo.SetVersion(PACKAGE_VERSION);
873 aboutInfo.SetDescription("A list manager for maintaining kit lists");
874 aboutInfo.SetCopyright("(C) 2008-2025");
875 aboutInfo.SetWebSite("https://www.fdsd.co.uk/kitlist");
876 aboutInfo.AddDeveloper("Frank Dean <frank.dean@fdsd.co.uk>");
877 wxAboutBox(aboutInfo);
878}
879
880void MyFrame::OnNewFile(wxCommandEvent& WXUNUSED(event))
881{
882 SetStatusText("");
883 if (gui_app->is_dirty()) {
884 if (wxMessageBox("Kitlist has not been saved! Continue?", "Please confirm",
885 wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT | wxCANCEL, this) != wxYES)
886 return;
887 }
888 gui_app->new_file();
891}
892
893void MyFrame::OnFileHistoryMenuItem(wxCommandEvent& event)
894{
895 SetStatusText("");
896 if (gui_app->is_dirty()) {
897 if (wxMessageBox("Kitlist has not been saved! Continue?", "Please confirm",
898 wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT | wxCANCEL, this) != wxYES)
899 return;
900 }
901 do_open(file_history->GetHistoryFile(event.GetId() - wxID_FILE1));
902}
903
904void MyFrame::OnOpen(wxCommandEvent& WXUNUSED(event))
905{
906 SetStatusText("");
907 if (gui_app->is_dirty()) {
908 if (wxMessageBox("Kitlist has not been saved! Continue?", "Please confirm",
909 wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT | wxCANCEL, this) != wxYES)
910 return;
911 }
912 wxFileDialog openFileDialog(
913 this, "Open Kitlist file", "", "",
914 "Kitlist files (*.kit)|*.kit|XML files (*.xml)|*.xml|All files|*",
915 wxFD_OPEN | wxFD_FILE_MUST_EXIST | wxFD_CHANGE_DIR);
916
917 if (openFileDialog.ShowModal() == wxID_CANCEL)
918 return;
919
920 do_open(openFileDialog.GetPath());
921}
922
923void MyFrame::do_open(wxString wx_path)
924{
925 try {
926 const string path = wx_path.ToStdString();
927 gui_app->load_file(path);
928 file_history->AddFileToHistory(wx_path);
929 file_menu->Enable(file_history_menu_item->GetId(), true);
932 } catch (const KitListGui::file_not_found& e) {
933 wxLogError("File not found: %s", wx_path);
934 wxMessageDialog dlg(this, wxString(e.what()), "File not found", wxICON_ERROR);
935 dlg.ShowModal();
936 } catch (const KitParser::parse_exception& e) {
937 wxLogError("Failure loading \"%s\": %s", wx_path, wxString::FromUTF8(e.what()));
938 wxMessageDialog dlg(this, wxString(e.what()), wxMessageBoxCaptionStr, wxICON_ERROR);
939 dlg.ShowModal();
940 }
941}
942
945void MyFrame::load_file(std::string filename)
946{
947 gui_app->load_file(filename);
948}
949
950void MyFrame::OnSave(wxCommandEvent& WXUNUSED(event))
951{
952 SetStatusText("");
953 save();
954}
955
956void MyFrame::OnSaveAs(wxCommandEvent& WXUNUSED(event))
957{
958 SetStatusText("");
959 wxFileDialog saveFileDialog(
960 this, "Save Kitlist file", "", "",
961 "Kitlist files (*.kit)|*.kit",
962 wxFD_SAVE | wxFD_CHANGE_DIR | wxFD_OVERWRITE_PROMPT);
963
964 if (saveFileDialog.ShowModal() == wxID_CANCEL)
965 return;
966
967 wxString wx_path = saveFileDialog.GetPath();
968 if (!wx_path.ends_with(KitListBaseApp::DEFAULT_FILE_EXTENSION))
970 // wxLogDebug("Chose file: \"%s\"", path);
971
972 gui_app->set_filename(wx_path.ToStdString());
973 save();
974 file_history->AddFileToHistory(wx_path);
975 file_menu->Enable(file_history_menu_item->GetId(), true);
976}
977
978void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event) )
979{
980 if (gui_app->is_dirty()) {
981 if (wxMessageBox("Kitlist has not been saved! Quit anyway?", "Please confirm",
982 wxICON_QUESTION | wxYES_NO | wxNO_DEFAULT| wxCANCEL, this) != wxYES)
983 return;
984 }
985 auto config = wxConfig::Get();
986 const auto old_path = config->GetPath();
987 config->SetPath("/FileHistory");
988 file_history->Save(*config);
989 config->SetPath(old_path);
990 Close(true);
991}
992
993void MyFrame::OnAddItem(wxCommandEvent& WXUNUSED(event))
994{
995 ItemDialog dlg(this, "New item");
996 if (dlg.ShowModal() == wxID_CANCEL)
997 return;
998
999 const string name = dlg.get_name();
1000 if (name.empty())
1001 return;
1002
1003 bool checked = dlg.is_checked();
1004 gui_app->new_item(name, checked);
1006}
1007
1008void MyFrame::OnAddCategory(wxCommandEvent& WXUNUSED(event))
1009{
1010 auto d = wxTextEntryDialog(this, "Category name", "New Category");
1011 if (d.ShowModal() == wxID_CANCEL)
1012 return;
1013 const string name = d.GetValue().ToStdString();
1014 if (name.empty())
1015 return;
1016 auto cat_id = gui_app->new_category(name);
1017 gui_app->select_category(cat_id);
1018 const auto row = populate_category_list(cat_id);
1020 category_list_ctrl->SelectRow(row);
1021}
1022
1024{
1025 bool result = gui_app->save();
1026 if (result) {
1027 SetStatusText("Saved");
1028 } else {
1029 SetStatusText("Save failed");
1030 }
1031}
1032
1033void MyFrame::update_file_menu(bool is_dirty)
1034{
1035 if (!file_menu)
1036 return;
1037 file_menu->Enable(SAVE, is_dirty && !gui_app->get_filename().empty());
1038}
1039
1041{
1042 if (!edit_menu)
1043 return;
1044 const bool have_selected_items = item_list_ctrl->HasSelection();
1045 const auto visible_item_count = item_list_ctrl->GetItemCount();
1046 const bool have_visible_items = visible_item_count != 0;
1047 const bool have_checked_items = gui_app->get_current_checked_item_count();
1048 const bool is_category_selected = gui_app->get_selected_category() != Model::no_category;
1049 edit_menu->Enable(SELECT_ALL_ITEMS, have_visible_items);
1050 edit_menu->Enable(UNSELECT_ALL_ITEMS, have_visible_items && have_selected_items);
1051 edit_menu->Enable(CHECK_ALL, have_visible_items);
1052 edit_menu->Enable(UNCHECK_ALL, have_visible_items);
1053 edit_menu->Enable(TOGGLE_ALL, have_visible_items);
1054 edit_menu->Enable(COPY_CHECKED_TO_CATEGORIES, have_checked_items);
1055
1056 edit_menu->Enable(CHECK_SELECTED, have_selected_items);
1057 edit_menu->Enable(UNCHECK_SELECTED, have_selected_items);
1058 edit_menu->Enable(TOGGLE_SELECTED, have_selected_items);
1060 have_selected_items &&
1061 is_category_selected);
1063 have_visible_items &&
1064 is_category_selected);
1066 have_visible_items &&
1067 !is_category_selected);
1068 edit_menu->Enable(COPY_SELECTED_ITEMS_TO_CATEGORIES, have_selected_items);
1069}
1070
1071// ----------------------------------------------------------------------------
1072// ItemDialog
1073// ----------------------------------------------------------------------------
1074
1075ItemDialog::ItemDialog(wxWindow* parent, const wxString& title)
1076 : wxDialog(parent, wxID_ANY, title,
1077 wxDefaultPosition, wxDefaultSize,
1078 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
1079 checkbox_ctrl(),
1080 name_ctrl() {
1081
1082 wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
1083 wxBoxSizer* item_options_sizer = new wxBoxSizer(wxHORIZONTAL);
1084 checkbox_ctrl = new wxCheckBox(this, wxID_ANY, "Checked",
1085 wxDefaultPosition, wxDefaultSize,
1086 wxCHK_2STATE | wxALIGN_RIGHT);
1087 item_options_sizer->Add(checkbox_ctrl,
1088 wxSizerFlags().Border().Center());
1089 item_options_sizer->Add(new wxStaticText(this, wxID_ANY, "Item name"),
1090 wxSizerFlags().Border().Center());
1091 name_ctrl = new wxTextCtrl(this, wxID_ANY);
1092 auto min_size = name_ctrl->GetMinSize();
1093 min_size.SetWidth(180);
1094 name_ctrl->SetMinSize(min_size);
1095 item_options_sizer->Add(name_ctrl, wxSizerFlags().Proportion(1).Center());
1096 main_sizer->Add(item_options_sizer, wxSizerFlags().DoubleBorder().Expand().Proportion(1));
1097 wxSizer* button_sizer = CreateSeparatedButtonSizer( wxOK|wxCANCEL );
1098 main_sizer->Add(button_sizer, wxSizerFlags().Border().Expand());
1099 SetSizerAndFit( main_sizer );
1100 Center();
1101}
A dialog for editing or creating an Item.
ItemDialog(wxWindow *parent, const wxString &title)
wxTextCtrl * name_ctrl
The input control for the item name.
const std::string get_name() const
The name.
bool is_checked() const
Whether checkbox was checked.
wxCheckBox * checkbox_ctrl
The checkbox control.
Exception throw when a file is not found.
virtual const char * what() const override
Describes the exeption.
const std::string get_filename() const
std::string filename
The current filename the Model was loaded from.
virtual void load_file(const std::string &filename)
Loads the specified file.
void set_filename(const std::string &filename)
Sets a new filename to associate with the Model.
static const std::string DEFAULT_FILE_EXTENSION
The application's default filename extension. .kit.
bool is_dirty() const
Implements a GUI front-end for the application using wxWidgets.
MyFrame * my_frame
The main frame of the application.
virtual bool OnCmdLineParsed(wxCmdLineParser &parser) override
Called after the command line has been successfully parsed.
virtual void OnInitCmdLine(wxCmdLineParser &parser) override
Called from OnInit() and initializes the parser with the command line options for the application.
virtual int OnExit() override
Can be used to cleanup items created during OnInit().
virtual void update_menu_state(bool is_dirty) override
Enables or disables various menu options, as appropriate.
virtual void category_entry_changed(const std::shared_ptr< Category > category) override
Called whenever a Category name changes.
virtual bool OnInit() override
Initializes the application's main window.
virtual void load_file(const std::string &filename) override
Loads the specified file.
virtual void item_entry_changed(const std::shared_ptr< Item > item) override
Called whenever a Item name changes.
XML parsing error.
Definition kitparser.hpp:86
check_action
Specifies what state Item checkmarks must be changed to.
Definition model.hpp:117
@ check
Definition model.hpp:119
@ uncheck
Definition model.hpp:120
@ toggle
Definition model.hpp:118
static const int32_t no_category
Indicates no filtering by category.
Definition model.hpp:153
Implements the main frame of the GUI.
void OnShowAll(wxCommandEvent &WXUNUSED(event))
Menu event handler for showing all items.
void OnUncheckSelected(wxCommandEvent &event)
Menu handler called to mark all selected items as unchecked.
void delete_category(int32_t id)
Deletes the specified Category from the model.
void OnRemoveCheckedItems(wxCommandEvent &WXUNUSED(event))
Menu handler called to remove checked items from a Category.
unsigned populate_category_list(int32_t select_category=Model::no_category)
Rebuilds the list of categories.
void OnUncheckAll(wxCommandEvent &WXUNUSED(event))
Menu handler called to uncheck all items in the list.
wxMenu * file_menu
The File menu.
void OnSave(wxCommandEvent &event)
Menu event handler to save the current file.
void OnNewFile(wxCommandEvent &event)
Menu event handler for creating a new blank model.
static const int ITEM_NAME_COL
The zero based indexed position of the item name column.
void OnCategoryContextMenu(wxDataViewEvent &event)
Implements a pop-up context menu for the Category list.
void OnCheckSelected(wxCommandEvent &event)
Menu handler called to mark all selected items as checked.
void OnUnselectAllItems(wxCommandEvent &WXUNUSED(event))
Menu event handler for unselecting all items.
void OnPrintPreview(wxCommandEvent &event)
Provides a print previews of the list of items.
void CopyCheckedToCategories(wxCommandEvent &WXUNUSED(event))
Menu handler called to copy checked items to one or more categories.
void do_open(wxString wx_path)
Opens the file from the specified path.
void OnToggleAll(wxCommandEvent &WXUNUSED(event))
Menu handler called to toggle the checked state of all items in the list.
wxDataViewListCtrl * category_list_ctrl
The Category list control.
void change_selected_checked_states(Model::check_action action)
Switches the checked state of all the currently selected items in the item list.
void OnItemContextMenu(wxDataViewEvent &event)
Implements a pop-up context menu for the Item list.
std::unique_ptr< std::exception > last_error
Holds the most recent exception that occurred or nullptr if no exception.
static const int MIN_PANE_SIZE
The minimum size that the split window can be changed to by the user.
void delete_item(int32_t id)
Deletes the specified Item from the model.
void OnAddCategory(wxCommandEvent &WXUNUSED(event))
Menu event handler for creating a new Category.
void update_file_menu(bool is_dirty)
KitListGui * gui_app
Pointer to the main GUI class.
void OnShowUnchecked(wxCommandEvent &WXUNUSED(event))
Menu event handler for showing only unchecked items.
void OnCategoryStartEditing(wxDataViewEvent &event)
Event handler implemented to disallow editing the first row of the Category list.
void load_file(const std::string filename)
Loads the specified file.
virtual ~MyFrame()
Destructor.
void OnOpen(wxCommandEvent &event)
Menu event handler to open another file.
void setup_rich_text_printing()
Sets up rich text printing.
static const int CATEGORY_NAME_COL
The zero based indexed of the category name column.
void remove_item(int32_t id)
Removes references to the item from the currently selected Category.
void selected_item_changed()
Disables or enables menu items, depending on whether any items have been selected in the Item list.
void OnFileHistoryMenuItem(wxCommandEvent &event)
Menu event handler to open a file that has been selected from the recent files menu.
std::unique_ptr< wxRichTextPrinting > rich_text_printing
Pointer to an instance supporting rich text printing.
wxFileHistory * file_history
The list of recent files.
wxMenu * edit_menu
The Edit menu.
void OnAddItem(wxCommandEvent &WXUNUSED(event))
Menu event handler for creating a new Item.
wxMenuItem * file_history_menu_item
A pointer to the recent files menu item.
void OnPrint(wxCommandEvent &event)
Prints the list of items.
void OnQuit(wxCommandEvent &event)
Called when the user chooses to quit the application.
void OnCheckAll(wxCommandEvent &WXUNUSED(event))
Menu handler called to check all items in the list.
wxDataViewListCtrl * item_list_ctrl
Pointer to the Item list controller.
void OnShow(wxShowEvent &event)
Event handler for when the window is shown.
void OnCopySelectedItemsToCategories(wxCommandEvent &event)
Menu handler called to copy selected items to one or more categories.
void OnShowChecked(wxCommandEvent &WXUNUSED(event))
Menu event handler for showing only checked items.
void FillBuffer(wxRichTextBuffer &buf) const
Fills the passed buffer with rich text to print or preview.
void OnSelectItem(wxDataViewEvent &event)
Event handler called when a user selects a Item from the list. Calls selected_item_changed().
void OnAbout(wxCommandEvent &event)
Menu event handler showing an About dialog box.
void OnSelectAllItems(wxCommandEvent &WXUNUSED(event))
Menu event handler for selecting all items.
void OnRemoveSelectedItems(wxCommandEvent &event)
Menu handler called to remove selected items from a Category.
void populate_item_list()
Rebuilds the list of items.
static const int VSPLIT_DIST
The initial width of the category column.
void update_edit_menu()
Enables or disables menu items on the Edit menu.
void OnToggleSelected(wxCommandEvent &event)
Menu handler called to toggle the checked state of all selected items.
void select_and_copy_to_categories(std::vector< std::shared_ptr< Item > > selected_items)
Displays a dialog for user to select target categories to copy selected items to.
void OnItemListValueChanged(wxDataViewEvent &event)
Event handler called when the Item name is edited.
void save()
Saves the current Model using the current KitListBaseApp::filename.
static const int ITEM_CHECKED_COL
The zero based indexed position of the item checked column.
void OnDeleteCheckedItems(wxCommandEvent &WXUNUSED(event))
Menu handler called to delete checked items from the model.
void OnSelectCategory(wxDataViewEvent &event)
Event handler called when a user selects a Category from the list.
void OnCategoryListValueChanged(wxDataViewEvent &event)
Event handler called when the Category name is edited.
void OnSaveAs(wxCommandEvent &event)
Menu event handler to save the model to a new file.
Implements the application's split window.
void OnPositionChanged(wxSplitterEvent &event)
Implements a workaround on Linux to initialize the split window correctly on startup.
Main Text-based User Interface (FINALCUT) for the application.
int run(int argc, char *argv[], const std::string filename)
Runs the FINAL CUT text based user interface.
Implements an FTXUI text based user interface.
int run(const std::string filename)
The main run loop.
wxBEGIN_EVENT_TABLE(MySplitterWindow, wxSplitterWindow) wxEND_EVENT_TABLE() void MySplitterWindow
@ CATEGORY_CONTEXT_MENU
@ SELECT_CATEGORY
@ DELETE_ITEM
@ ITEM_CONTEXT_MENU
@ REMOVE_ITEM
@ ITEM_CHANGE
@ CHECK_SELECTED
@ CATEGORY_ITEM_CHANGE
@ ADD_ITEM
@ TOGGLE_SELECTED
@ SAVE_AS
@ SELECT_ALL_ITEMS
@ REMOVE_CHECKED_ITEMS
@ COPY_SELECTED_ITEMS_TO_CATEGORIES
@ QUIT
@ SAVE
@ REMOVE_SELECTED_ITEMS
@ CHECK_ALL
@ SHOW_UNCHECKED_EVENT
@ NEW_FILE
@ SHOW_ALL_EVENT
@ COPY_CHECKED_TO_CATEGORIES
@ DELETE_CATEGORY
@ UNSELECT_ALL_ITEMS
@ TOGGLE_ALL
@ COPY_ITEM_TO_CATEGORIES
@ ADD_CATEGORY
@ OPEN_FILE
@ DELETE_CHECKED_ITEMS
@ SHOW_CHECKED_EVENT
@ UNCHECK_ALL
@ UNCHECK_SELECTED
@ CATEGORY_ITEM_START_EDITING
@ SELECT_ITEM
Copyright 2008-2025 Frank Dean