diff --git a/ChangeLog b/ChangeLog index 84950070..9d026976 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,7 @@ +2017-01-30 Markus Gans + * Refactoring FLabel::draw + * Refactoring FFileDialog::readDir + 2017-01-28 Markus Gans * Refactoring FApplication::processKeyboardEvent * Shorter methods and a fix for recreating new windows diff --git a/examples/ui.cpp b/examples/ui.cpp index 3aaaee49..c5b956fa 100644 --- a/examples/ui.cpp +++ b/examples/ui.cpp @@ -277,6 +277,10 @@ class MyDialog : public FDialog void initStatusBar(); void initStatusBarCallbacks(); void initWidgets(); + void initFlatButtons(); + void initToggleButtons(); + void initButtons(); + void initLabels(); void initWidgetsCallbacks(); void adjustSize(); @@ -531,7 +535,7 @@ void MyDialog::initStatusBar() //---------------------------------------------------------------------- void MyDialog::initStatusBarCallbacks() { - // Add some function callbacks + // Add statusbar function callbacks key_F1->addCallback ( @@ -555,7 +559,40 @@ void MyDialog::initStatusBarCallbacks() //---------------------------------------------------------------------- void MyDialog::initWidgets() { + // Flat buttons + initFlatButtons(); + + // Radio buttons and check boxes + initToggleButtons(); + + // A text input field + myLineEdit = new FLineEdit (this); + myLineEdit->setGeometry(22, 1, 10, 1); + myLineEdit->setLabelText (L"&Input"); + myLineEdit->setStatusbarMessage ("Press Enter to set the title"); + *myLineEdit << FString("EnTry").toLower(); + // Buttons + initButtons(); + + // A multiple selection listbox + myList = new FListBox (this); + myList->setGeometry(38, 1, 14, 17); + myList->setText ("Items"); + myList->setStatusbarMessage ("99 items in a list"); + myList->setMultiSelection(); + + for (int z = 1; z < 100; z++) + myList->insert (FString() << z << L" placeholder"); + + // Text labels + initLabels(); +} + +//---------------------------------------------------------------------- +void MyDialog::initFlatButtons() +{ + // Flat buttons MyButton1 = new FButton (this); MyButton1->setGeometry(3, 3, 5, 1); MyButton1->setText (L"&SIN"); @@ -579,6 +616,29 @@ void MyDialog::initWidgets() MyButton3->setNoUnderline(); MyButton3->setFlat(); + // Add button callback functions + MyButton1->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) + ); + + MyButton2->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) + ); + + MyButton3->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) + ); +} + +//---------------------------------------------------------------------- +void MyDialog::initToggleButtons() +{ // Radio buttons in a group FButtonGroup* radioButtonGroup = new FButtonGroup ("Button", this); radioButtonGroup->setGeometry(3, 8, 14, 4); @@ -607,14 +667,11 @@ void MyDialog::initWidgets() check2->setGeometry(1, 2, 9, 1); check2->setChecked(); check2->setNoUnderline(); +} - // A text input field - myLineEdit = new FLineEdit (this); - myLineEdit->setGeometry(22, 1, 10, 1); - myLineEdit->setLabelText (L"&Input"); - myLineEdit->setStatusbarMessage ("Press Enter to set the title"); - *myLineEdit << FString("EnTry").toLower(); - +//---------------------------------------------------------------------- +void MyDialog::initButtons() +{ // Buttons MyButton4 = new FButton (this); MyButton4->setGeometry(20, 8, 12, 1); @@ -634,16 +691,30 @@ void MyDialog::initWidgets() MyButton6->setStatusbarMessage ("Exit the program"); MyButton6->addAccelerator('x'); - // A multiple selection listbox - myList = new FListBox (this); - myList->setGeometry(38, 1, 14, 17); - myList->setText ("Items"); - myList->setStatusbarMessage ("99 items in a list"); - myList->setMultiSelection(); + // Add button callback functions + MyButton4->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &MyDialog::cb_input2buttonText), + static_cast(myLineEdit) + ); - for (int z = 1; z < 100; z++) - myList->insert (FString() << z << L" placeholder"); + MyButton5->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &MyDialog::cb_ProgressBar) + ); + MyButton6->addCallback + ( + "clicked", + F_METHOD_CALLBACK (this, &FApplication::cb_exitApp) + ); +} + +//---------------------------------------------------------------------- +void MyDialog::initLabels() +{ // Text labels FLabel* headline = new FLabel (this); headline->setGeometry(21, 3, 10, 1); @@ -672,43 +743,6 @@ void MyDialog::initWidgetsCallbacks() { // Add some function callbacks - MyButton1->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) - ); - - MyButton2->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) - ); - - MyButton3->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &MyDialog::cb_noFunctionMsg) - ); - - MyButton4->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &MyDialog::cb_input2buttonText), - static_cast(myLineEdit) - ); - - MyButton5->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &MyDialog::cb_ProgressBar) - ); - - MyButton6->addCallback - ( - "clicked", - F_METHOD_CALLBACK (this, &FApplication::cb_exitApp) - ); - myLineEdit->addCallback ( "activate", // e.g. on diff --git a/include/final/ffiledialog.h b/include/final/ffiledialog.h index f49ad038..0aedb9e7 100644 --- a/include/final/ffiledialog.h +++ b/include/final/ffiledialog.h @@ -3,7 +3,7 @@ * * * This file is part of the Final Cut widget toolkit * * * -* Copyright 2014-2017 Markus Gans * +* Copyright 2014-2018 Markus Gans * * * * The Final Cut is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * @@ -130,7 +130,6 @@ class FFileDialog : public FDialog void onKeyPress (FKeyEvent*); // Methods - int readDir(); static const FString fileOpenChooser ( FWidget* , const FString& = FString() , const FString& = FString() ); @@ -166,6 +165,10 @@ class FFileDialog : public FDialog void clear(); int numOfDirs(); void sortDir(); + int readDir(); + void getEntry (struct dirent*); + void followSymLink (dir_entry&); + void dirEntriesToList(); int changeDir (const FString&); void printPath (const FString&); static const FString getHomeDir(); diff --git a/include/final/flabel.h b/include/final/flabel.h index dda96b3e..e36beada 100644 --- a/include/final/flabel.h +++ b/include/final/flabel.h @@ -3,7 +3,7 @@ * * * This file is part of the Final Cut widget toolkit * * * -* Copyright 2014-2017 Markus Gans * +* Copyright 2014-2018 Markus Gans * * * * The Final Cut is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * @@ -141,8 +141,10 @@ class FLabel : public FWidget int getHotkeyPos (wchar_t[], wchar_t[], uInt); void setHotkeyAccelerator(); int getAlignOffset (int); - void printLine (wchar_t[], uInt, int, int = 0); void draw(); + void drawMultiLine(); + void drawSingleLine(); + void printLine (wchar_t[], uInt, int, int = 0); // Data Members FStringList multiline_text; diff --git a/src/ffiledialog.cpp b/src/ffiledialog.cpp index 355df4b6..067e22b1 100644 --- a/src/ffiledialog.cpp +++ b/src/ffiledialog.cpp @@ -3,7 +3,7 @@ * * * This file is part of the Final Cut widget toolkit * * * -* Copyright 2014-2017 Markus Gans * +* Copyright 2014-2018 Markus Gans * * * * The Final Cut is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * @@ -254,136 +254,6 @@ void FFileDialog::onKeyPress (FKeyEvent* ev) } } -//---------------------------------------------------------------------- -int FFileDialog::readDir() -{ - const char* const dir = directory.c_str(); - const char* const filter = filter_pattern.c_str(); - directory_stream = opendir(dir); - - if ( ! directory_stream ) - { - FMessageBox::error (this, "Can't open directory\n" + directory); - return -1; - } - - clear(); - - while ( true ) - { - errno = 0; - struct dirent* next = readdir(directory_stream); - - if ( next ) - { - if ( next->d_name[0] == '.' && next->d_name[1] == '\0' ) - continue; - - if ( ! show_hidden - && next->d_name[0] == '.' - && next->d_name[1] != '\0' - && next->d_name[1] != '.' ) - { - continue; - } - - if ( dir[0] == '/' && dir[1] == '\0' - && std::strcmp(next->d_name, "..") == 0 ) - continue; - - dir_entry entry; - entry.name = strdup(next->d_name); - -#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE - entry.fifo = (next->d_type & DT_FIFO) == DT_FIFO; - entry.character_device = (next->d_type & DT_CHR ) == DT_CHR; - entry.directory = (next->d_type & DT_DIR ) == DT_DIR; - entry.block_device = (next->d_type & DT_BLK ) == DT_BLK; - entry.regular_file = (next->d_type & DT_REG ) == DT_REG; - entry.symbolic_link = (next->d_type & DT_LNK ) == DT_LNK; - entry.socket = (next->d_type & DT_SOCK) == DT_SOCK; -#else - struct stat s; - stat (entry.name, &s); - entry.fifo = S_ISFIFO (s.st_mode); - entry.character_device = S_ISCHR (s.st_mode); - entry.directory = S_ISDIR (s.st_mode); - entry.block_device = S_ISBLK (s.st_mode); - entry.regular_file = S_ISREG (s.st_mode); - entry.symbolic_link = S_ISLNK (s.st_mode); - entry.socket = S_ISSOCK (s.st_mode); -#endif - - if ( entry.symbolic_link ) // symbolic link - { - char resolved_path[MAXPATHLEN] = {}; - char symLink[MAXPATHLEN] = {}; - std::strncpy (symLink, dir, sizeof(symLink) - 1); - std::strncat ( symLink - , next->d_name - , sizeof(symLink) - std::strlen(symLink) - 1); - - if ( realpath(symLink, resolved_path) != 0 ) // follow link - { - struct stat sb; - - if ( lstat(resolved_path, &sb) == 0 ) - { - if ( S_ISDIR(sb.st_mode) ) - entry.directory = true; - } - } - } - - if ( entry.directory ) - dir_entries.push_back (entry); - else if ( pattern_match(filter, entry.name) ) - dir_entries.push_back (entry); - else - std::free(entry.name); - } - else if ( errno != 0 ) - { - FMessageBox::error (this, "Reading directory\n" + directory); - - if ( errno == EOVERFLOW ) // Value too large to be stored in data type - break; - } - else - break; - } // end while - - if ( closedir(directory_stream) != 0 ) - { - FMessageBox::error (this, "Closing directory\n" + directory); - return -2; - } - - sortDir(); - - // fill list with directory entries - filebrowser->clear(); - - if ( ! dir_entries.empty() ) - { - std::vector::const_iterator iter, last; - iter = dir_entries.begin(); - last = dir_entries.end(); - - while ( iter != last ) - { - if ( (*iter).directory ) - filebrowser->insert(FString((*iter).name), fc::SquareBrackets); - else - filebrowser->insert(FString((*iter).name)); - - ++iter; - } - } - - return 0; -} - //---------------------------------------------------------------------- const FString FFileDialog::fileOpenChooser ( FWidget* parent , const FString& dirname @@ -701,6 +571,159 @@ void FFileDialog::sortDir() , sortByName ); } +//---------------------------------------------------------------------- +int FFileDialog::readDir() +{ + const char* const dir = directory.c_str(); + directory_stream = opendir(dir); + + if ( ! directory_stream ) + { + FMessageBox::error (this, "Can't open directory\n" + directory); + return -1; + } + + clear(); + + while ( true ) + { + errno = 0; + struct dirent* next = readdir(directory_stream); + + if ( next ) + { + // Continue if name = "." (current directory) + if ( next->d_name[0] == '.' && next->d_name[1] == '\0' ) + continue; + + // Skip hidden entries + if ( ! show_hidden + && next->d_name[0] == '.' + && next->d_name[1] != '\0' + && next->d_name[1] != '.' ) + continue; + + // Skip ".." for the root directory + if ( dir[0] == '/' && dir[1] == '\0' + && std::strcmp(next->d_name, "..") == 0 ) + continue; + + getEntry(next); + } + else if ( errno != 0 ) + { + FMessageBox::error (this, "Reading directory\n" + directory); + + if ( errno == EOVERFLOW ) // Value too large to be stored in data type + break; + } + else + break; + } // end while + + if ( closedir(directory_stream) != 0 ) + { + FMessageBox::error (this, "Closing directory\n" + directory); + return -2; + } + + sortDir(); + + // Insert directory entries into the list + dirEntriesToList(); + + return 0; +} + +//---------------------------------------------------------------------- +void FFileDialog::getEntry (struct dirent* d_entry) +{ + const char* const filter = filter_pattern.c_str(); + dir_entry entry; + + entry.name = strdup(d_entry->d_name); + +#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE + entry.fifo = (d_entry->d_type & DT_FIFO) == DT_FIFO; + entry.character_device = (d_entry->d_type & DT_CHR ) == DT_CHR; + entry.directory = (d_entry->d_type & DT_DIR ) == DT_DIR; + entry.block_device = (d_entry->d_type & DT_BLK ) == DT_BLK; + entry.regular_file = (d_entry->d_type & DT_REG ) == DT_REG; + entry.symbolic_link = (d_entry->d_type & DT_LNK ) == DT_LNK; + entry.socket = (d_entry->d_type & DT_SOCK) == DT_SOCK; +#else + struct stat s; + stat (entry.name, &s); + entry.fifo = S_ISFIFO (s.st_mode); + entry.character_device = S_ISCHR (s.st_mode); + entry.directory = S_ISDIR (s.st_mode); + entry.block_device = S_ISBLK (s.st_mode); + entry.regular_file = S_ISREG (s.st_mode); + entry.symbolic_link = S_ISLNK (s.st_mode); + entry.socket = S_ISSOCK (s.st_mode); +#endif + + followSymLink(entry); + + if ( entry.directory ) + dir_entries.push_back (entry); + else if ( pattern_match(filter, entry.name) ) + dir_entries.push_back (entry); + else + std::free(entry.name); +} + +//---------------------------------------------------------------------- +void FFileDialog::followSymLink (dir_entry& entry) +{ + if ( ! entry.symbolic_link ) + return; // No symbolic link + + char resolved_path[MAXPATHLEN] = {}; + char symLink[MAXPATHLEN] = {}; + struct stat sb; + const char* const dir = directory.c_str(); + + std::strncpy (symLink, dir, sizeof(symLink) - 1); + std::strncat ( symLink + , entry.name + , sizeof(symLink) - std::strlen(symLink) - 1); + + if ( realpath(symLink, resolved_path) == 0 ) + return; // Cannot follow the symlink + + if ( lstat(resolved_path, &sb) == -1 ) + return; // Cannot get file status + + if ( S_ISDIR(sb.st_mode) ) + entry.directory = true; +} + +//---------------------------------------------------------------------- +void FFileDialog::dirEntriesToList() +{ + // Fill list with directory entries + + filebrowser->clear(); + + if ( dir_entries.empty() ) + return; + + std::vector::const_iterator iter, last; + iter = dir_entries.begin(); + last = dir_entries.end(); + + while ( iter != last ) + { + if ( (*iter).directory ) + filebrowser->insert(FString((*iter).name), fc::SquareBrackets); + else + filebrowser->insert(FString((*iter).name)); + + ++iter; + } +} + //---------------------------------------------------------------------- int FFileDialog::changeDir (const FString& dirname) { diff --git a/src/flabel.cpp b/src/flabel.cpp index 458cf5b7..2b3f9961 100644 --- a/src/flabel.cpp +++ b/src/flabel.cpp @@ -3,7 +3,7 @@ * * * This file is part of the Final Cut widget toolkit * * * -* Copyright 2014-2017 Markus Gans * +* Copyright 2014-2018 Markus Gans * * * * The Final Cut is free software; you can redistribute it and/or * * modify it under the terms of the GNU Lesser General Public License * @@ -486,6 +486,117 @@ int FLabel::getAlignOffset (int length) return 0; } +//---------------------------------------------------------------------- +void FLabel::draw() +{ + if ( isMonochron() ) + { + setReverse(true); + + if ( hasEmphasis() ) + setBold(true); + } + + if ( hasEmphasis() ) + setColor (emphasis_color, getBackgroundColor()); + else + setColor(); + + // Draw the text + if ( multiline && getHeight() >= 2 ) + drawMultiLine(); + else + drawSingleLine(); + + if ( isMonochron() ) + { + setReverse(false); + + if ( hasEmphasis() ) + setBold(false); + } +} + +//---------------------------------------------------------------------- +void FLabel::drawMultiLine() +{ + uInt y = 0; + uInt text_lines = uInt(multiline_text.size()); + + + while ( y < text_lines && y < uInt(getHeight()) ) + { + wchar_t* label_text; + bool hotkey_printed = false; + int align_offset, hotkeypos = -1; + uInt length = multiline_text[y].getLength(); + + try + { + label_text = new wchar_t[length + 1](); + } + catch (const std::bad_alloc& ex) + { + std::cerr << "not enough memory to alloc " << ex.what() << std::endl; + return; + } + + wchar_t* src = const_cast(multiline_text[y].wc_str()); + wchar_t* dest = const_cast(label_text); + + if ( ! hotkey_printed ) + hotkeypos = getHotkeyPos(src, dest, length); + else + std::wcsncpy(dest, src, length); + + setPrintPos (1, 1 + int(y)); + + if ( hotkeypos != -1 ) + { + align_offset = getAlignOffset (int(length - 1)); + printLine (label_text, length - 1, hotkeypos, align_offset); + hotkey_printed = true; + hotkeypos = -1; + } + else + { + align_offset = getAlignOffset (int(length)); + printLine (label_text, length, -1, align_offset); + } + + y++; + delete[] label_text; + } +} + +//---------------------------------------------------------------------- +void FLabel::drawSingleLine() +{ + wchar_t* label_text; + int hotkeypos = -1, align_offset; + uInt length = text.getLength(); + + try + { + label_text = new wchar_t[length + 1](); + } + catch (const std::bad_alloc& ex) + { + std::cerr << "not enough memory to alloc " << ex.what() << std::endl; + return; + } + + hotkeypos = getHotkeyPos (text.wc_str(), label_text, length); + + if ( hotkeypos != -1 ) + length--; + + setPrintPos (1,1); + align_offset = getAlignOffset (int(length)); + printLine (label_text, length, hotkeypos, align_offset); + delete[] label_text; +} + //---------------------------------------------------------------------- void FLabel::printLine ( wchar_t line[] , uInt length @@ -557,106 +668,3 @@ void FLabel::printLine ( wchar_t line[] if ( hasReverseMode() ) setReverse(false); } - -//---------------------------------------------------------------------- -void FLabel::draw() -{ - wchar_t* label_text; - uInt length; - int hotkeypos, align_offset; - - if ( isMonochron() ) - { - setReverse(true); - - if ( hasEmphasis() ) - setBold(true); - } - - if ( hasEmphasis() ) - setColor (emphasis_color, getBackgroundColor()); - else - setColor(); - - hotkeypos = -1; - - if ( multiline && getHeight() >= 2 ) - { - uInt y = 0; - uInt text_lines = uInt(multiline_text.size()); - bool hotkey_printed = false; - - while ( y < text_lines && y < uInt(getHeight()) ) - { - length = multiline_text[y].getLength(); - - try - { - label_text = new wchar_t[length + 1](); - } - catch (const std::bad_alloc& ex) - { - std::cerr << "not enough memory to alloc " << ex.what() << std::endl; - return; - } - - wchar_t* src = const_cast(multiline_text[y].wc_str()); - wchar_t* dest = const_cast(label_text); - - if ( ! hotkey_printed ) - hotkeypos = getHotkeyPos(src, dest, length); - else - std::wcsncpy(dest, src, length); - - setPrintPos (1, 1 + int(y)); - - if ( hotkeypos != -1 ) - { - align_offset = getAlignOffset (int(length - 1)); - printLine (label_text, length - 1, hotkeypos, align_offset); - hotkey_printed = true; - hotkeypos = -1; - } - else - { - align_offset = getAlignOffset (int(length)); - printLine (label_text, length, -1, align_offset); - } - - y++; - delete[] label_text; - } - } - else - { - length = text.getLength(); - - try - { - label_text = new wchar_t[length + 1](); - } - catch (const std::bad_alloc& ex) - { - std::cerr << "not enough memory to alloc " << ex.what() << std::endl; - return; - } - - hotkeypos = getHotkeyPos (text.wc_str(), label_text, length); - - if ( hotkeypos != -1 ) - length--; - - setPrintPos (1,1); - align_offset = getAlignOffset (int(length)); - printLine (label_text, length, hotkeypos, align_offset); - delete[] label_text; - } - - if ( isMonochron() ) - { - setReverse(false); - - if ( hasEmphasis() ) - setBold(false); - } -}