Last active
July 8, 2016 19:55
-
-
Save MattSturgeon/5cadb42f8caa126e0afa21d55f868eff to your computer and use it in GitHub Desktop.
Extract filetype filters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
demo | |
*.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <gtk/gtk.h> | |
#include <string> | |
#include <array> | |
#include <cstring> // For memcpy | |
#include <iostream> // For cerr (if unknown special) | |
#include "platform.h" | |
/* Add a GtkFileFilter object for each row in f_types to the given GtkFileChooser */ | |
void add_gtk_filters(GtkFileChooser *dialog) { | |
for (size_t i = 0; i < f_types.size(); i++) { | |
GtkFileFilter *filter = gtk_file_filter_new(); | |
/* Set the name */ | |
gtk_file_filter_set_name(filter, f_types.at(i).name.c_str()); | |
if (f_types.at(i).special) { | |
/* Special cases */ | |
if (f_types.at(i).type == "ALL_TYPES") { | |
/* For ALL_TYPES use "*" pattern to match any file */ | |
gtk_file_filter_add_pattern(filter, "*"); | |
} | |
else if (f_types.at(i).type == "ALL_SUPPORTED") { | |
/* For ALL_SUPPORTED add a pattern for each supported file type */ | |
for (size_t j = 0; j < f_types.size(); j++) { | |
if (! f_types.at(j).special) { | |
// Use standard pattern "*.type" | |
gtk_file_filter_add_pattern(filter, ("*." + f_types.at(j).type).c_str()); | |
} | |
} | |
} | |
else { | |
/* Unknown special! Just continue to the next file_type. */ | |
std::cerr << "Warning: Unknown special file_type " << f_types.at(i).type << "at index " << i << std::endl; | |
// Free filter? | |
continue; | |
} | |
} | |
else { | |
/* Normal values */ | |
// For normal file types, just add the standard pattern "*.type" | |
gtk_file_filter_add_pattern(filter, ("*." + f_types.at(i).type).c_str()); | |
} | |
// Add the filter | |
gtk_file_chooser_add_filter(dialog, filter); | |
} | |
} | |
char *show_file_picker() { | |
char *path = nullptr; | |
GtkWidget *parent, *dialog; | |
if (!gtk_init_check(NULL, NULL)) | |
return nullptr; | |
parent = gtk_window_new(GTK_WINDOW_TOPLEVEL); | |
dialog = gtk_file_chooser_dialog_new( "Open File", | |
GTK_WINDOW(parent), | |
GTK_FILE_CHOOSER_ACTION_OPEN, | |
"_Cancel", GTK_RESPONSE_CANCEL, | |
"_Open", GTK_RESPONSE_ACCEPT, | |
NULL ); | |
// Filter file types | |
add_gtk_filters(GTK_FILE_CHOOSER (dialog)); // NOTE: This is the only change to this function | |
if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_ACCEPT) { | |
char *filename = nullptr; | |
filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog)); | |
size_t len = strlen(filename); | |
path = (char *)malloc(len+1); | |
memcpy(path, filename, len+1); | |
if (!path ) { | |
g_free(filename); | |
gtk_widget_destroy(dialog); | |
return nullptr; | |
} | |
g_free(filename); | |
} | |
while (gtk_events_pending()) | |
gtk_main_iteration(); | |
gtk_widget_destroy(dialog); | |
while (gtk_events_pending()) | |
gtk_main_iteration(); | |
return path; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//#include "platform.h" | |
#include <array> | |
#include <string> | |
#include "platform.h" | |
// For debugging (std::cout, std::cerr) | |
#include <iostream> | |
#include <cstdio> | |
int main() { | |
/* First print our internal data */ | |
printf("\nPrinting std::array <file_type> f_types...\n\n"); | |
// Print entire contents of f_types | |
std::cout << "f_types[" << f_types.size() << "]: {{" << std::endl; | |
for (size_t i = 0; i < f_types.size(); i++) { | |
std::cout << " " << i << ": {" << std::endl | |
<< " name: \"" << f_types.at(i).name << "\"," << std::endl | |
<< " type: \"" << f_types.at(i).type << "\"," << std::endl | |
<< " special: " << f_types.at(i).special << "," << std::endl | |
<< " }," << std::endl; | |
} | |
std::cout << "}}" << std::endl; | |
/* Next generate and print lpstrFilter */ | |
printf("\n\nRunning (win32) gen_lpstrFilter...\n"); | |
// Alocate 512 bytes to out | |
size_t winFilterArr_len = 512; | |
char winFilterArr[winFilterArr_len] = {0}; | |
size_t bytes_written = gen_lpstrFilter(winFilterArr, winFilterArr_len); | |
// Print each string from winFilterArr until we hit an empty string | |
printf("\n\nwinFilterArr[%lu of %lu]:\n \"", bytes_written, winFilterArr_len); | |
size_t i = 0, x = 0; | |
int y = 1, z; | |
while (winFilterArr[x] || winFilterArr[x-1]) { // If x and x-1 point to '\0' this is an empty string | |
// Print each char, replace '\0' with \n | |
if (winFilterArr[x]) | |
// Print the x'th char from winFilterArr. | |
fwrite( &winFilterArr[x], sizeof(winFilterArr[x]), 1, stdout); | |
else{ | |
// "End" the string | |
printf("\""); | |
// If not the last, prep the next string (indentation and quotes) | |
if (winFilterArr[x+1]) { | |
if (i % 2 > 0) printf("\n \""); | |
else { | |
// Align second string ~36 chars from begining of left string | |
z = 36 - y; if (z < 1) z = 1; | |
while (--z) printf(" "); | |
printf("\""); | |
} | |
} | |
i++; y = 0; | |
} | |
x++; y++; | |
} | |
printf("\n"); | |
/* Next Create a Gtk file picker and print the returned path to stdout */ | |
printf("\n\nRunning (gtk) show_file_picker... \n\n"); | |
printf("Gtk filepath: \"%s\"\n\n", show_file_picker()); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
CC=g++ | |
CPPFLAGS=-I. `pkg-config --cflags --libs gtk+-3.0` | |
demo: main.o gtk.o win32.o | |
$(CC) $(CPPFLAGS) -o demo main.o gtk.o win32.o -I. | |
clean: | |
rm -vf demo *.o |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef PLATFORM_H | |
#define PLATFORM_H | |
#include <array> | |
#include <string> | |
/* | |
* file_type is used to describe a file type to be filtered by the | |
* platform's "open" dialog. | |
* | |
* 'name' is the user-visible description given for the generated filter. | |
* 'type' is the file extension that should be filtered. | |
* 'special' should be true if mapping functions need to write code specific | |
* to generating a filter for this file_type (e.g. ALL_TYPES). | |
* | |
*/ | |
struct file_type { | |
const std::string type; | |
const std::string name; | |
const bool special; | |
}; | |
/* | |
* f_types is an array of file_type structs. | |
* | |
* The first entries are special, and have special handling rules in the | |
* various mapping functions. | |
* | |
*/ | |
static std::array <file_type, 5> f_types = {{ | |
/* Special values */ | |
{ | |
/* ALL_SUPPORTED should match all non-special f_types in this array. */ | |
type: "ALL_SUPPORTED", name: "Supported files", | |
special: true, | |
},{ | |
/* ALL_TYPES should match any file */ | |
type: "ALL_TYPES", name: "All files", | |
special: true, // If OSX supports '*', this will not need to be special | |
}, | |
/* Normal filters */ | |
{ type: "brd", name: "BRD files - the origionals!", }, | |
{ type: "bdv", name: "BDV files are cool too...", }, | |
{ type: "fz", name: "FZ... Ok, who even is this guy?", }, | |
}}; | |
/* Function declarations for the main.cpp demo */ | |
size_t gen_lpstrFilter (char * out, size_t out_len); | |
char *show_file_picker(); | |
#endif /* end of include guard: PLATFORM_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <string> | |
#include <array> | |
#include <cstring> // For memcpy | |
#include <iostream> // For cerr (if unknown special) | |
#include "platform.h" | |
/* Generate a windows lpstrFilter array for show_file_picker. | |
* | |
* out's type should probably be corrected to follow standard windows | |
* definition LPSTR. | |
* | |
* Writes a packed array of pairs of null terminated char arrays. | |
* Each pair is a Filter name and a filter list. | |
* After the final pair an empty string ("\0") is added to terminate | |
* the array. | |
* | |
* Takes a char pointer and the max number of bytes to write. | |
* | |
* Will check to make sure it has enough space before writing each pair. | |
* If it runs out of space it will print to stderr and exit (1). This | |
* should probably be amended to show a dialog warning and then return 0. | |
* returns the number of bytes written to out | |
*/ | |
size_t gen_lpstrFilter (char * out, size_t out_len) { | |
// Keep track of how much we've written to out. | |
// We use this to decide where to write the next string | |
// and to double check we have enough memory left. | |
size_t x = 0; | |
// lpstrFilter is a blob of memory containing null-terninated strings | |
// with an extra null to terminate the blob. | |
// i.e. it's a packed-array of null-terninated strings with a zero-length | |
// string at the end. | |
// It's read as pairs of strings, with the first being the name and the | |
// second being a semi-colon seperated list of filters. | |
// Iterate over each file_type | |
for (size_t i = 0; i < f_types.size(); i++) { | |
// Set the name and define filter for current item | |
std::string name = f_types.at(i).name; | |
std::string filter; | |
if (f_types.at(i).special) { | |
/* Special cases */ | |
if (f_types.at(i).type == "ALL_TYPES") { | |
/* For ALL_TYPES use "*" filter to match any file */ | |
filter = "*"; | |
} | |
else if (f_types.at(i).type == "ALL_SUPPORTED") { | |
/* For ALL_SUPPORTED create a list each supported file type */ | |
for (size_t j = 0; j < f_types.size(); j++) { | |
// Only add non-special items | |
if (! f_types.at(j).special){ | |
// Add ";*.file_type" to filter | |
if (filter.length()) filter += ";"; // Don't prefix ';' to the first item | |
filter += "*." + f_types.at(j).type; | |
} | |
} | |
} | |
else { | |
/* Unknown special! Just continue to the next file_type. */ | |
std::cerr << "Warning: Unknown special file_type " << f_types.at(i).type << "at index " << i << std::endl; | |
continue; | |
} | |
} | |
else { | |
/* Normal values */ | |
// For normal file types, just add the standard pattern "*.type" | |
filter = "*." + f_types.at(i).type; | |
} | |
// Make sure we have enouth space left in 'out' to fit 'filter', 'name' and three '\0's. | |
// Two of the '\0's terminate the strings (filter and name - string::length doesnt count '\0's) | |
// The third terminates the empty string that ends the lpstrFilter array. | |
if (x >= out_len - (filter.length() + name.length() + 3)) { // +3 null chars (1 ends filter, 1 ends name, 1 ends the array) | |
// Run out of buffer memory!!! | |
fprintf(stderr, "Error: Ran out of memory to store out!\n i: '%lu', x: '%lu', out_len: '%lu'\n", i, x, out_len); | |
exit (1); | |
} | |
// Copy the pair of strings | |
// | |
// (char *)& out[x] creates a char pointer to x bytes after the start of out | |
// memcpy then copies name to position-x | |
// finally we add the number of bytes written, to x (+1 for null-ternination) | |
// First string is name | |
memcpy((char *)&out[x], name.c_str(), name.length()); | |
x += name.length() + 1; | |
// Second string is filter | |
memcpy((char *)&out[x], filter.c_str(), filter.length()); | |
x += filter.length() + 1; | |
} | |
// Ensure there is a final empty string to terminate the packed array | |
memcpy((char *)&out[x], "", 1); | |
x++; | |
return x; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
For Mac OS X you can do something like this: (diff from Charliebruce's repo, master branch)
However, (as always with Apple) it's not like on the other platforms: it can only "grey out" the files with extension not in the filter list and you can't select a specific filter. Selecting a specific filter could be implemented with the help of an Accessory View, but it wouldn't be really useful.
PS: note the use of a range-based for loop. A pretty nice feature of C++11.