diff --git a/.codedocs b/.codedocs index 94853e96..ef859181 100644 --- a/.codedocs +++ b/.codedocs @@ -1,4 +1,4 @@ -PROJECT_NAME = "The Final Cut" +PROJECT_NAME = "FINAL CUT" EXCLUDE = debian, doc, icon, logo, m4, scripts, examples EXCLUDE_PATTERNS = */test/* diff --git a/ChangeLog b/ChangeLog index 22cc301d..ccd563be 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,35 @@ +2019-01-03 Markus Gans + * Improved PC encoding for Cygwin and Linux + * Integration of an output filter to replace missing characters + * Better Linux console support for UTF-8 encoding + (Default is PC charset encoding) + +2018-12-31 Markus Gans + * Use the override specifier + +2018-12-30 Markus Gans + * Cygwin compiled fix for C++11 + +2018-12-29 Markus Gans + * Text scrolling in FTextView was broken since February 17th! + * Replace redundant FString code with templates + +2018-12-28 Markus Gans + * Add the assignment operator (=) for FButton to set the button text + * Corrected shortening of overlong texts in the title bar of FDialog + * Add a "signals and callbacks" chapter into the first steps document + +2018-12-25 Markus Gans + * Add a "event processing" chapter into the first steps document + +2018-12-24 Markus Gans + * Events can not only be sent to FWidgets, but also to FObjects + * New event FUserEvent for user-defined events + +2018-12-19 Markus Gans + * Use of smart pointers + * Add a "memory management" chapter into the first steps document + 2018-12-17 Markus Gans * Improve FButton mouse click animation * Minor data type corrections @@ -5,7 +37,7 @@ 2018-12-15 Markus Gans * Use of the C++11 auto specifier in the program code - * Code reduction by using of Range-based for loop + * Code reduction by using of range-based for loop * The example program for video attributes now replaces the switch statement with a vector of lambda expressions diff --git a/Makefile.am b/Makefile.am index d6c1f007..0b88c7dc 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ #---------------------------------------------------------------------- -# Makefile.am - The Final Cut terminal programming library +# Makefile.am - FINAL CUT terminal programming library #---------------------------------------------------------------------- AUTOMAKE_OPTIONS = foreign diff --git a/configure.ac b/configure.ac index 514a960b..7fe23f2a 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ #---------------------------------------------------------------------- -# configure.ac - the Final Cut library +# configure.ac - FINAL CUT library #---------------------------------------------------------------------- # Process this file with autoconf to produce a configure script. diff --git a/doc/Makefile.am b/doc/Makefile.am index e092c40a..90b9f8dd 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,5 +1,5 @@ #---------------------------------------------------------------------- -# Makefile.am - the Final Cut library +# Makefile.am - FINAL CUT library #---------------------------------------------------------------------- docdir = ${datadir}/doc/${PACKAGE} diff --git a/doc/first-steps.md b/doc/first-steps.md index b31cd5bf..4d198b0c 100644 --- a/doc/first-steps.md +++ b/doc/first-steps.md @@ -97,3 +97,550 @@ the result to the operating system. The started application enters the main event loop. This loop does not end until the window is not closed. + +Memory Management +----------------- + +To create a hierarchy of FObjects (or derived classes/widgets), +a new FObject must initialize with its parent object. + +```cpp +FObject* parent = new FObject(); +FObject* child = new FObject(parent); +``` + +To deallocate the used memory of a parent FObject, the allocated memory +of its child objects will also automatically deallocate. + +An object can also be assigned to another object later via `addChild()`. + +```cpp +FObject* parent = new FObject(); +FObject* child = new FObject(); +parent->addChild(child); +``` + +The child object assignment can also remove at any time with +`delChild()`. + +```cpp +FObject* parent = new FObject(); +FObject* child = new FObject(parent); +parent->delChild(child); +``` + +If an FObject with a parent will remove from the hierarchy, +the destructor automatically deletes the object assignment from +its parent object. If a class object doesn't derive from FObject, +you must implement storage deallocation yourself. + +**File:** *memory.cpp* +```cpp +#include + +using namespace finalcut; + +int main (int argc, char* argv[]) +{ + FApplication app(argc, argv); + + // The object dialog is managed by app + FDialog* dialog = new FDialog(&app); + dialog->setText ("Window Title"); + dialog->setGeometry (25, 5, 40, 8); + + // The object input is managed by dialog + FLineEdit* input = new FLineEdit("predefined text", dialog); + input->setGeometry(8, 2, 29, 1); + input->setLabelText (L"&Input"); + + // The object label is managed by dialog + FLabel* label = new FLabel ( "Lorem ipsum dolor sit amet, consectetur " + "adipiscing elit, sed do eiusmod tempor " + "incididunt ut labore et dolore magna aliqua." + , dialog ); + label->setGeometry (2, 4, 36, 1); + app.setMainWidget(dialog); + dialog->show(); + return app.exec(); +} +``` +*(Note: You can close the window with the mouse, +Shift+F10 or Ctrl+^)* + + +After entering the source code in *memory.cpp* you can compile +the above program with gcc: +```cpp +g++ -O2 -lfinal memory.cpp -o memory +``` + + +Event Processing +---------------- + +Calling `FApplication::exec()` starts the FINAL CUT main event loop. +While the event loop is running, the system constantly checks whether +an event has occurred and sends it to the application's currently focused +object. The events of the terminal such as keystrokes, mouse actions or +resizing the terminal are translated into `FEvent` objects and sent it to +the active `FObject`. It is also possible to use `FApplication::sendEvent()` +or `FApplication::queueEvent()` to send your own events to an object. + +`FObject`-derived objects process incoming events by reimplementing the +virtual method `event()`. The `FObject` itself calls only +`onTimer()` or `onUserEvent()` and ignores all other events. The +`FObject`-derived class `FWidget` also reimplements the `event()` method +to handle further events. `FWidget` calls the `FWidget::onKeyPress` method +when you press a key, or the `FWidget::onMouseDown` method when you click +a mouse button. + + +### Event handler reimplementation ### + +An event in FINAL CUT is an object that inherits from the base class +`FEvent`. There are several event types, represented by an enum value. +For example, the method `FEvent::type()` returns the type +`fc::MouseDown_Event` when you press down a mouse button. + +Some event types have data that cannot store in an `FEvent` object. +For example, a click event of the mouse must store which button it +triggered where the mouse pointer was at that time. In classes derived +from `FEvent`, such as `FMouseEvent()`, we store this data. + +Widgets get their events from the `event()` method inherited from FObject. +The implementation of `event()` in `FWidget` forwards the most common event +types to specific event handlers such as `FMouseEvent()`, `FKeyEvent()` or +`FResizeEvent()`. There are many other event types. It is also possible to +create own event types and send them to other objects. + + +**The FINAL CUT event types:** +```cpp +enum events +{ + None_Event, // invalid event + KeyPress_Event, // key pressed + KeyUp_Event, // key released + KeyDown_Event, // key pressed + MouseDown_Event, // mouse button pressed + MouseUp_Event, // mouse button released + MouseDoubleClick_Event, // mouse button double click + MouseWheel_Event, // mouse wheel rolled + MouseMove_Event, // mouse move + FocusIn_Event, // focus in + FocusOut_Event, // focus out + ChildFocusIn_Event, // child focus in + ChildFocusOut_Event, // child focus out + WindowActive_Event, // activate window + WindowInactive_Event, // deactivate window + WindowRaised_Event, // raise window + WindowLowered_Event, // lower window + Accelerator_Event, // keyboard accelerator + Resize_Event, // terminal resize + Show_Event, // widget is shown + Hide_Event, // widget is hidden + Close_Event, // widget close + Timer_Event, // timer event occur + User_Event // user defined event +}; +``` + + +**File:** *timer.cpp* +```cpp +#include + +using namespace finalcut; + +class dialogWidget : public FDialog +{ + public: + explicit dialogWidget (FWidget* parent = nullptr) + : FDialog(parent) + { + setText ("Dialog"); + setGeometry (25, 5, 23, 4); + label.setGeometry (1, 1, 10, 1); + label.setAlignment (fc::alignRight); + value.setGeometry (11, 1, 10, 1); + id = addTimer(100); + } + + private: + virtual void onTimer (FTimerEvent* ev) + { + if ( id == ev->getTimerId() && n < 9999999999 ) + { + value.setNumber(n); + value.redraw(); + n++; + } + } + + FLabel label{"Counter: ", this}; + FLabel value{"0", this}; + long n{0}; + int id{0}; +}; + +int main (int argc, char* argv[]) +{ + FApplication app(argc, argv); + dialogWidget dialog(&app); + app.setMainWidget(&dialog); + dialog.show(); + return app.exec(); +} +``` +*(Note: You can close the window with the mouse, +Shift+F10 or Ctrl+^)* + + +After entering the source code in *timer.cpp* you can compile +the above program with gcc: +```cpp +g++ -O2 -std=c++11 -lfinal timer.cpp -o timer +``` + + +Signals and Callbacks +--------------------- + +The callback mechanism is essential for developing applications with +FINAL CUT. Callback routines allow the programmer to connect different +objects (which do not need to know each other). Connected objects notify +each other when an action occurs in a widget. To uniquely identify a widget +action, it uses signal strings. For example, if an `FButton` object gets +clicked by a keyboard or mouse, it sends the string "clicked". A signal +handler explicitly provided by Widget, in the form of a callback function +or a callback method, can react to such a signal. + +A callback function is always structured as follows: + +```cpp +void cb_function (FWidget* w, FDataPtr data) +{...} +``` + +The structure of a callback method is the same: + +```cpp +void classname::cb_methode (FWidget* w, FDataPtr data) +{...} +``` + +We use the `addCallback()` method of the `FWidget` class to connect +to other widget objects. + +For calling functions and static methods: + +```cpp +void FWidget::addCallback ( const FString& cb_signal + , FCallback cb_handler + , FDataPtr data ) +{...} +``` + +For calling a member method of a specific instance: + +```cpp +void FWidget::addCallback ( const FString& cb_signal + , FWidget* cb_instance + , FMemberCallback cb_handler + , FDataPtr data ) +{...} +``` + +There are two macros `F_FUNCTION_CALLBACK` and `F_METHOD_CALLBACK` to avoid +having to deal with necessary type conversions. With `delCallback()` you can +remove a connection to a signal handler or a widget. Alternatively, you can +use `delCallbacks()` to remove all existing callbacks from an object. + + +### The FINAL CUT widgets emit the following default signals ### + +
+
FButton
+
"clicked"
+ +
FCheckMenuItem
+
"clicked"
"toggled"
+ +
FLineEdit
+
"activate"
"changed"
+ +
FListBox
+
"clicked"
"row-changed"
"row-selected"
+ +
FListView
+
"clicked"
"row-changed"
+ +
FMenu
+
"activate"
+ +
FMenuItem
+
"activate"
"clicked"
"deactivate"
+ +
FRadioMenuItem
+
"clicked"
"toggled"
+ +
FScrollbar
+
"change-value"
+ +
FStatusBar
+
"activate"
+ +
FTextView
+
"changed"
+ +
FToggleButton
+
"clicked"
"toggled"
+
+ +  + +### Example of a callback function: ### + +**File:** *callback-function.cpp* + +```cpp +#include + +using namespace finalcut; + +void cb_changeText (FWidget* w, FDataPtr data) +{ + FButton& button = *(static_cast(w)); + FLabel& label = *(static_cast(data)); + label.clear(); + label << "The " << button.getClassName() << " was pressed"; + label.redraw(); +} + +int main (int argc, char* argv[]) +{ + FApplication app(argc, argv); + FDialog dialog(&app); + dialog.setText ("A dialog with callback function"); + dialog.setGeometry (25, 5, 45, 9); + FLabel label (&dialog); + label = "The button has never been pressed before"; + label.setGeometry (2, 2, 41, 1); + FButton button (&dialog); + // Character follows '&' will be used as the accelerator key + button = "&Click me"; + button.setGeometry (15, 5, 14, 1); + + // Connect the button signal "clicked" with the callback function + button.addCallback + ( + "clicked", + F_FUNCTION_CALLBACK (&cb_changeText), + &label + ); + + app.setMainWidget(&dialog); + dialog.show(); + return app.exec(); +} +``` +*(Note: You can close the dialog with the mouse, +Shift+F10 or Ctrl+^)* + + +After entering the source code in *callback-function.cpp* you can compile +the above program with gcc: +```cpp +g++ -O2 -lfinal callback-function.cpp -o callback-function +``` +  + +### Example of a callback method: ### + +**File:** *callback-method.cpp* + +```cpp +#include + +using namespace finalcut; + +class dialogWidget : public FDialog +{ + public: + explicit dialogWidget (FWidget* parent = nullptr) + : FDialog(parent) + { + setText ("Callback method"); + setGeometry (25, 5, 25, 7); + button.setGeometry (7, 3, 10, 1); + + // Connect the button signal "clicked" with the callback method + button.addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &FApplication::cb_exitApp), + nullptr + ); + } + + private: + FButton button{"&Quit", this}; +}; + +int main (int argc, char* argv[]) +{ + FApplication app(argc, argv); + dialogWidget dialog(&app); + app.setMainWidget(&dialog); + dialog.show(); + return app.exec(); +} +``` +*(Note: You can close the window with the mouse, +Shift+F10 or Ctrl+^)* + + +After entering the source code in *callback-method.cpp* you can compile +the above program with gcc: +```cpp +g++ -O2 -std=c++11 -lfinal callback-method.cpp -o callback-method +``` +  + + +### Send custom signals ### + +You can use the `emitCallback()` method to generate a user-defined signal. +You can connect this signal later with the method `addCallback()` to a +self-defined routine. + +**File:** *emit-signal.cpp* +```cpp +#include + +using namespace finalcut; + +class dialogWidget : public FDialog +{ + public: + explicit dialogWidget (FWidget* parent = nullptr) + : FDialog(parent) + { + setGeometry (25, 5, 22, 7); + setText ("Emit signal"); + label.setGeometry (8, 1, 5, 1); + label.setAlignment (fc::alignRight); + label.setForegroundColor (fc::Black); + plus.setGeometry (3, 3, 5, 1); + minus.setGeometry (13, 3, 5, 1); + plus.setNoUnderline(); + minus.setNoUnderline(); + + // Connect the button signal "clicked" with the callback method + plus.addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &dialogWidget::cb_plus) + ); + + minus.addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &dialogWidget::cb_minus) + ); + + // Connect own signals + addCallback + ( + "hot", + F_METHOD_CALLBACK (this, &dialogWidget::cb_set_red) + ); + + addCallback + ( + "regular", + F_METHOD_CALLBACK (this, &dialogWidget::cb_set_black) + ); + + addCallback + ( + "cold", + F_METHOD_CALLBACK (this, &dialogWidget::cb_set_blue) + ); + } + + private: + void cb_plus (FWidget*, FDataPtr) + { + if ( t < 100 ) + t++; + + if ( t == 30 ) + emitCallback("hot"); + else if ( t == 1 ) + emitCallback("regular"); + + setTemperature(); + } + + void cb_minus (FWidget*, FDataPtr) + { + if ( t > -99 ) + t--; + + if ( t == 0 ) + emitCallback("cold"); + else if ( t == 29 ) + emitCallback("regular"); + + setTemperature(); + } + + void cb_set_blue (FWidget*, FDataPtr) + { + label.setForegroundColor (fc::Blue); + } + + void cb_set_black (FWidget*, FDataPtr) + { + label.setForegroundColor (fc::Black); + } + + void cb_set_red (FWidget*, FDataPtr) + { + label.setForegroundColor (fc::Red); + } + + void setTemperature() + { + label.clear(); + label << t << "°C"; + label.redraw(); + } + + int t = 20; + FLabel label{FString() << t << "°C", this}; + FButton plus {"&+", this}; + FButton minus {"&-", this}; +}; + +int main (int argc, char* argv[]) +{ + FApplication app(argc, argv); + dialogWidget dialog(&app); + app.setMainWidget(&dialog); + dialog.show(); + return app.exec(); +} +``` +*(Note: You can close the window with the mouse, +Shift+F10 or Ctrl+^)* + + +After entering the source code in *emit-signal.cpp* you can compile +the above program with gcc: +```cpp +g++ -O2 -std=c++11 -lfinal emit-signal.cpp -o emit-signal +``` +The FINAL CUT widgets emit the following default signals: \ No newline at end of file diff --git a/examples/Makefile.clang b/examples/Makefile.clang index 12856a99..3d094f1c 100644 --- a/examples/Makefile.clang +++ b/examples/Makefile.clang @@ -1,5 +1,5 @@ #----------------------------------------------------------------------------- -# Makefile for Final Cut +# Makefile for FINAL CUT #----------------------------------------------------------------------------- # This is where make install will install the executable diff --git a/examples/Makefile.gcc b/examples/Makefile.gcc index 94146f54..072db0da 100644 --- a/examples/Makefile.gcc +++ b/examples/Makefile.gcc @@ -1,5 +1,5 @@ #----------------------------------------------------------------------------- -# Makefile for Final Cut +# Makefile for FINAL CUT #----------------------------------------------------------------------------- # This is where make install will install the executable diff --git a/examples/calculator.cpp b/examples/calculator.cpp index d579714e..be6929a8 100644 --- a/examples/calculator.cpp +++ b/examples/calculator.cpp @@ -25,11 +25,12 @@ #include #include #include +#include #include #include -const lDouble PI = 3.141592653589793238L; +constexpr lDouble PI = 3.141592653589793238L; //---------------------------------------------------------------------- @@ -49,7 +50,7 @@ class Button : public finalcut::FButton void setChecked(bool); // Event handler - virtual void onKeyPress (finalcut::FKeyEvent*); + virtual void onKeyPress (finalcut::FKeyEvent*) override; private: // Data Member @@ -63,12 +64,12 @@ Button::Button (finalcut::FWidget* parent) { } //---------------------------------------------------------------------- -void Button::setChecked (bool on) +void Button::setChecked (bool enable) { - if ( checked == on ) + if ( checked == enable ) return; - checked = on; + checked = enable; if ( checked ) { @@ -117,12 +118,12 @@ class Calc : public finalcut::FDialog ~Calc(); // Event handlers - virtual void onKeyPress (finalcut::FKeyEvent*); - virtual void onAccel (finalcut::FAccelEvent*); - virtual void onClose (finalcut::FCloseEvent*); + virtual void onKeyPress (finalcut::FKeyEvent*) override; + virtual void onAccel (finalcut::FAccelEvent*) override; + virtual void onClose (finalcut::FCloseEvent*) override; // Callback method - void cb_buttonClicked (finalcut::FWidget*, data_ptr); + void cb_buttonClicked (finalcut::FWidget*, FDataPtr); private: // Typedef and Enumeration @@ -169,7 +170,7 @@ class Calc : public finalcut::FDialog // Methods void drawDispay(); - virtual void draw(); + virtual void draw() override; void clear (lDouble&); void zero (lDouble&); void one (lDouble&); @@ -211,8 +212,8 @@ class Calc : public finalcut::FDialog void setInfixOperator (char); void clearInfixOperator(); void calcInfixOperator(); - virtual void adjustSize(); - const wchar_t* getButtonText (int); + virtual void adjustSize() override; + const wchar_t* getButtonText (std::size_t); void mapKeyFunctions(); // Data Members @@ -227,7 +228,7 @@ class Calc : public finalcut::FDialog char infix_operator{'\0'}; char last_infix_operator{'\0'}; finalcut::FString input{""}; - int button_no[Calc::NUM_OF_BUTTONS]{}; + std::size_t button_no[Calc::NUM_OF_BUTTONS]{}; struct stack_data { @@ -236,7 +237,7 @@ class Calc : public finalcut::FDialog }; std::stack bracket_stack{}; - std::map calculator_buttons{}; + std::map > calculator_buttons{}; std::map key_map{}; }; #pragma pack(pop) @@ -252,19 +253,20 @@ Calc::Calc (FWidget* parent) setGeometry (19, 6, 37, 18); addAccelerator('q'); // Press 'q' to quit - for (int key = 0; key < Calc::NUM_OF_BUTTONS; key++) + for (std::size_t key = 0; key < Calc::NUM_OF_BUTTONS; key++) { - auto btn = new Button(this); + auto btn = std::make_shared