diff --git a/doxygen/cairo-renderer.dox b/doxygen/cairo-renderer.dox index e6cafd8..bf7c9ac 100644 --- a/doxygen/cairo-renderer.dox +++ b/doxygen/cairo-renderer.dox @@ -1,4 +1,4 @@ /** * @defgroup Cairo-Renderer Cairo Renderer - * @ingroup renderers + * @ingroup GdsOutputRenderer */ diff --git a/doxygen/external-renderer.dox b/doxygen/external-renderer.dox index 4d03c51..e7bbad5 100644 --- a/doxygen/external-renderer.dox +++ b/doxygen/external-renderer.dox @@ -1,4 +1,15 @@ /** - * @defgroup external-renderer External Shared Object Renderer - * @ingroup renderers + * @defgroup ExternalRenderer External Shared Object Renderer + * @ingroup GdsOutputRenderer + * + * @subsection ExternalRendererProps Properties + * This class inherits all properties from its parent @ref GdsOutputRenderer. + * In addition to that, it implements the following properties: + * + * Property Name | Description + * -----------------|---------------------------------------------------------------- + * shared-object-path | Path to the shared object used for rendering + * + * All these properties have to be set for rendering. + * */ diff --git a/doxygen/gds-output-renderer.dox b/doxygen/gds-output-renderer.dox new file mode 100644 index 0000000..6d2e083 --- /dev/null +++ b/doxygen/gds-output-renderer.dox @@ -0,0 +1,34 @@ +/** + * @defgroup GdsOutputRenderer GDS Output Renderer base class + * + * The renderers are used to convert the cell structures read from the GDS layout file + * into different output formats. + * + * The GdsOutputRenderer base class is used to derive all renderers from. + * + * @warning Although the GdsOutputRenderer class provides compatibility for asynchronous rendering, + * the class is not thread safe / re-entrant. Only use it from a signle context. Not even the rendering function called is allowed to modifiy this object. + * + * A allowed function to be called from the async rendering thread is #gds_output_renderer_update_gui_status_from_async and the get functions for the properties. + * + * @note The context that owned the renderer has to ensure that only one rendering is active at a time for a single instance of a renderer. + * + * By default this class implements the following features: + * + * @subsection GdsOutputRendererProps Properties + * Property Name | Description + * -----------------|---------------------------------------------------------------- + * layer-settings | LayerSettings object containing the layer rendering information + * output-file | Output file name for rendering + * + * All these properties have to be set for rendering. + * + * @subsection GdsOutputRendererSignals Signals / Events + * Signal Name | Description | Callback prototype + * -----------------|-------------------------------------------------|----------------------------------------------------------- + * async-finished | The asynchronous rendering is finished | void callback(GdsOutputRenderer *src, gpointer user_data) + * progress-changed | The asynchronous rendering progress changed | void callback(GdsOutputRenderer *src, const char *progress, gpointer user_data) + * + * @note The `char *progress` supplied to the callback function must not be modified or freed. + * + */ diff --git a/doxygen/latex-renderer.dox b/doxygen/latex-renderer.dox index bafb197..ee73196 100644 --- a/doxygen/latex-renderer.dox +++ b/doxygen/latex-renderer.dox @@ -1,4 +1,16 @@ /** * @defgroup LaTeX-Renderer LaTeX/TikZ Renderer - * @ingroup renderers + * @ingroup GdsOutputRenderer + * + * This is the class implementing the Latex / tikz output rendering + + * @subsection LaTeXRendererProps Properties + * This class inherits all properties from its parent @ref GdsOutputRenderer. + * In addition to that, it implements the following properties: + * + * Property Name | Description + * -----------------|---------------------------------------------------------------- + * standalone | Configure output LaTeX document to be standalone compilable (requires standalone documentclass) + * pdf-layers | Create OCG layers in LaTeX output + * */ diff --git a/doxygen/renderers.dox b/doxygen/renderers.dox deleted file mode 100644 index 753c7ec..0000000 --- a/doxygen/renderers.dox +++ /dev/null @@ -1,9 +0,0 @@ -/** - * @defgroup renderers Output Renderers - * - * The renderers are used to convert the cell structures read from the GDS layout file - * into different output formats. - * - * Currently the renders are statically implemented without the use of GObjects. - * This will probably change in future releases in order to make it easier to integrate new rendering methods. - */ diff --git a/gds-render-gui.c b/gds-render-gui.c index 8e934c0..6520ae1 100644 --- a/gds-render-gui.c +++ b/gds-render-gui.c @@ -47,6 +47,11 @@ enum gds_render_gui_signal_sig_ids {SIGNAL_WINDOW_CLOSED = 0, SIGNAL_COUNT}; static guint gds_render_gui_signals[SIGNAL_COUNT]; +struct gui_button_states { + gboolean rendering_active; + gboolean valid_cell_selected; +}; + struct _GdsRenderGui { /* Parent GObject */ GObject parent; @@ -54,6 +59,9 @@ struct _GdsRenderGui { /* Custom fields */ GtkWindow *main_window; GtkWidget *convert_button; + GtkWidget *open_button; + GtkWidget *load_layer_button; + GtkWidget *save_layer_button; GtkTreeStore *cell_tree_store; GtkWidget *cell_search_entry; LayerSelector *layer_selector; @@ -62,6 +70,7 @@ struct _GdsRenderGui { ActivityBar *activity_status_bar; struct render_settings render_dialog_settings; ColorPalette *palette; + struct gui_button_states button_state_data; }; G_DEFINE_TYPE(GdsRenderGui, gds_render_gui, G_TYPE_OBJECT) @@ -240,6 +249,23 @@ end_destroy: gtk_widget_destroy(open_dialog); } +static void process_button_state_changes(GdsRenderGui *self) +{ + gboolean convert_button_state = FALSE; + gboolean open_gds_button_state = FALSE; + + /* Calculate states */ + if (!self->button_state_data.rendering_active) { + open_gds_button_state = TRUE; + if (self->button_state_data.valid_cell_selected) + convert_button_state = TRUE; + } + + /* Apply states */ + gtk_widget_set_sensitive(self->convert_button, convert_button_state); + gtk_widget_set_sensitive(self->open_button, open_gds_button_state); +} + /** * @brief Callback for auto coloring button * @param button @@ -254,6 +280,29 @@ static void on_auto_color_clicked(gpointer button, gpointer user) layer_selector_auto_color_layers(self->layer_selector, self->palette, 1.0); } +static void async_rendering_finished_callback(GdsOutputRenderer *renderer, gpointer gui) +{ + GdsRenderGui *self; + + self = RENDERER_GUI(gui); + + self->button_state_data.rendering_active = FALSE; + process_button_state_changes(self); + activity_bar_set_ready(self->activity_status_bar); + + g_object_unref(renderer); +} + +static void async_rendering_status_update_callback(GdsOutputRenderer *renderer, const char *status_message, gpointer data) +{ + GdsRenderGui *gui; + (void)renderer; + + gui = RENDERER_GUI(data); + + activity_bar_set_busy(gui->activity_status_bar, status_message); +} + /** * @brief Convert button callback * @param button @@ -283,6 +332,10 @@ static void on_convert_clicked(gpointer button, gpointer user) if (!self) return; + /* Abort if rendering is already active */ + if (self->button_state_data.rendering_active == TRUE) + return; + sett = &self->render_dialog_settings; /* Get selected cell */ @@ -369,16 +422,33 @@ static void on_convert_clicked(gpointer button, gpointer user) case RENDERER_CAIROGRAPHICS_PDF: render_engine = GDS_RENDER_OUTPUT_RENDERER(cairo_renderer_new_pdf()); break; + default: + /* Abort rendering */ + render_engine = NULL; + break; } if (render_engine) { gds_output_renderer_set_output_file(render_engine, file_name); gds_output_renderer_set_layer_settings(render_engine, layer_settings); + /* Prevent user from overwriting library or triggering additional conversion */ + self->button_state_data.rendering_active = TRUE; + process_button_state_changes(self); + g_signal_connect(render_engine, "async-finished", G_CALLBACK(async_rendering_finished_callback), + self); + + activity_bar_set_busy(self->activity_status_bar, "Rendering cell..."); /* TODO: Replace this with asynchronous rendering. However, this fixes issue #19 */ - gds_output_renderer_render_output(render_engine, cell_to_render, sett->scale); - g_object_unref(render_engine); + g_signal_connect(render_engine, "progress-changed", + G_CALLBACK(async_rendering_status_update_callback), self); + gds_output_renderer_render_output_async(render_engine, cell_to_render, sett->scale); + + + //self->button_state_data.rendering_active = FALSE; + + //g_object_unref(render_engine); } g_free(file_name); @@ -407,7 +477,6 @@ static void cell_tree_view_activated(gpointer tree_view, GtkTreePath *path, on_convert_clicked(NULL, user); } - /** * @brief Callback for cell-selection change event * @@ -423,10 +492,12 @@ static void cell_selection_changed(GtkTreeSelection *sel, GdsRenderGui *self) if (gtk_tree_selection_get_selected(sel, &model, &iter)) { /* Node selected. Show button */ - gtk_widget_set_sensitive(self->convert_button, TRUE); + self->button_state_data.valid_cell_selected = TRUE; } else { - gtk_widget_set_sensitive(self->convert_button, FALSE); + self->button_state_data.valid_cell_selected = FALSE; } + + process_button_state_changes(self); } static void sort_up_callback(GtkWidget *widget, gpointer user) @@ -466,6 +537,9 @@ static void gds_render_gui_dispose(GObject *gobject) g_clear_object(&self->cell_search_entry); g_clear_object(&self->activity_status_bar); g_clear_object(&self->palette); + g_clear_object(&self->load_layer_button); + g_clear_object(&self->save_layer_button); + g_clear_object(&self->open_button); if (self->main_window) { g_signal_handlers_destroy(self->main_window); @@ -521,7 +595,8 @@ static void gds_render_gui_init(GdsRenderGui *self) self->cell_tree_store = cell_selector_stores->base_store; self->main_window = GTK_WINDOW(gtk_builder_get_object(main_builder, "main-window")); - g_signal_connect(GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-gds")), + self->open_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-gds")); + g_signal_connect(self->open_button, "clicked", G_CALLBACK(on_load_gds), (gpointer)self); self->convert_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "convert-button")); @@ -550,11 +625,10 @@ static void gds_render_gui_init(GdsRenderGui *self) g_signal_connect(sort_down_button, "clicked", G_CALLBACK(sort_down_callback), self); /* Set buttons for loading and saving */ - layer_selector_set_load_mapping_button(self->layer_selector, - GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-mapping")), - self->main_window); - layer_selector_set_save_mapping_button(self->layer_selector, GTK_WIDGET(gtk_builder_get_object(main_builder, "button-save-mapping")), - self->main_window); + self->load_layer_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-load-mapping")); + self->save_layer_button = GTK_WIDGET(gtk_builder_get_object(main_builder, "button-save-mapping")); + layer_selector_set_load_mapping_button(self->layer_selector, self->load_layer_button, self->main_window); + layer_selector_set_save_mapping_button(self->layer_selector, self->save_layer_button, self->main_window); /* Connect delete-event */ g_signal_connect(GTK_WIDGET(self->main_window), "delete-event", @@ -579,6 +653,10 @@ static void gds_render_gui_init(GdsRenderGui *self) g_object_unref(main_builder); + /* Setup default button sensibility data */ + self->button_state_data.rendering_active = FALSE; + self->button_state_data.valid_cell_selected = FALSE; + /* Reference all objects referenced by this object */ g_object_ref(self->activity_status_bar); g_object_ref(self->main_window); @@ -588,6 +666,9 @@ static void gds_render_gui_init(GdsRenderGui *self) g_object_ref(self->cell_tree_store); g_object_ref(self->cell_search_entry); g_object_ref(self->palette); + g_object_ref(self->open_button); + g_object_ref(self->load_layer_button); + g_object_ref(self->save_layer_button); } GdsRenderGui *gds_render_gui_new() diff --git a/geometric/vector-operations.c b/geometric/vector-operations.c index e383cbd..b236c3f 100644 --- a/geometric/vector-operations.c +++ b/geometric/vector-operations.c @@ -33,7 +33,7 @@ #include -#define ABS_DBL(a) ((a) < 0 ? -(a) : (a)) +#define ABS_DBL(a) ((a) < 0.0 ? -(a) : (a)) double vector_2d_scalar_multipy(struct vector_2d *a, struct vector_2d *b) { diff --git a/include/gds-render/gds-utils/gds-types.h b/include/gds-render/gds-utils/gds-types.h index c4fd1b1..4e1e49f 100644 --- a/include/gds-render/gds-utils/gds-types.h +++ b/include/gds-render/gds-utils/gds-types.h @@ -58,7 +58,7 @@ enum graphics_type enum path_type {PATH_FLUSH = 0, PATH_ROUNDED = 1, PATH_SQUARED = 2}; /**< Path line caps */ /** - * @brief A point in the 2D plane. Sometimes references as vertex + * @brief A point in the 2D plane. Sometimes reffered to as vertex */ struct gds_point { int x; diff --git a/include/gds-render/output-renderers/gds-output-renderer.h b/include/gds-render/output-renderers/gds-output-renderer.h index 56be520..04e33b9 100644 --- a/include/gds-render/output-renderers/gds-output-renderer.h +++ b/include/gds-render/output-renderers/gds-output-renderer.h @@ -144,6 +144,16 @@ void gds_output_renderer_set_layer_settings(GdsOutputRenderer *renderer, LayerSe */ int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale); +/** + * @brief This function emits the 'progress-changed' in the thread/context that triggered an asynchronous rendering + * + * If the rendering is not asynchronous, this function has no effect. + * + * @param renderer GdsOutputrenderer object + * @param status Status to supply to signal emission + */ +void gds_output_renderer_update_gui_status_from_async(GdsOutputRenderer *renderer, const char *status); + G_END_DECLS #endif /* _GDS_OUTPUT_RENDERER_H_ */ diff --git a/include/gds-render/widgets/activity-bar.h b/include/gds-render/widgets/activity-bar.h index 37ef801..1f35797 100644 --- a/include/gds-render/widgets/activity-bar.h +++ b/include/gds-render/widgets/activity-bar.h @@ -53,6 +53,14 @@ ActivityBar *activity_bar_new(); */ void activity_bar_set_ready(ActivityBar *bar); +/** + * @brief Enable spinner and set \p text. If text is NULL, 'Working...' is displayed + * @param bar Activity bar object + * @param text Text to display, may be NULL + + */ +void activity_bar_set_busy(ActivityBar *bar, const char *text); + G_END_DECLS #endif /* __LAYER_ELEMENT_H__ */ diff --git a/layer/layer-info.c b/layer/layer-info.c index 723d032..6340c1c 100644 --- a/layer/layer-info.c +++ b/layer/layer-info.c @@ -39,9 +39,36 @@ static void layer_settings_init(LayerSettings *self) self->layer_infos = NULL; } +static void layer_info_delete_with_name(struct layer_info *const info) +{ + if (!info) + return; + + if (info->name) + free(info->name); + free(info); +} + +static void layer_settings_dispose(GObject *obj) +{ + LayerSettings *self; + + self = GDS_RENDER_LAYER_SETTINGS(obj); + + if (self->layer_infos) { + g_list_free_full(self->layer_infos, (GDestroyNotify)layer_info_delete_with_name); + self->layer_infos = NULL; + } + + G_OBJECT_CLASS(layer_settings_parent_class)->dispose(obj); +} + static void layer_settings_class_init(LayerSettingsClass *klass) { - (void)klass; + GObjectClass *oclass; + + oclass = G_OBJECT_CLASS(klass); + oclass->dispose = layer_settings_dispose; return; } @@ -76,16 +103,6 @@ static struct layer_info *layer_info_copy(const struct layer_info * const info) return copy; } -static void layer_info_delete_with_name(struct layer_info *const info) -{ - if (!info) - return; - - if (info->name) - free(info->name); - free(info); -} - LayerSettings *layer_settings_new() { return g_object_new(GDS_RENDER_TYPE_LAYER_SETTINGS, NULL); diff --git a/output-renderers/cairo-renderer.c b/output-renderers/cairo-renderer.c index 06b8361..6fabf5c 100644 --- a/output-renderers/cairo-renderer.c +++ b/output-renderers/cairo-renderer.c @@ -400,6 +400,7 @@ static int cairo_renderer_render_output(GdsOutputRenderer *renderer, else pdf_file = output_file; + gds_output_renderer_update_gui_status_from_async(renderer, "Rendering Cairo Output..."); ret = cairo_renderer_render_cell_to_vector_file(cell, layer_infos, pdf_file, svg_file, scale); if (settings) diff --git a/output-renderers/external-renderer.c b/output-renderers/external-renderer.c index ba0dd0c..876433e 100644 --- a/output-renderers/external-renderer.c +++ b/output-renderers/external-renderer.c @@ -50,6 +50,7 @@ G_DEFINE_TYPE(ExternalRenderer, external_renderer, GDS_RENDER_TYPE_OUTPUT_RENDER * @param toplevel_cell Cell to render * @param layer_info_list Layer information (Color etc.) * @param output_file Destination file + * @param scale the scaling value to scale the output cell down by. * @param so_path Path to shared object * @return 0 if successful */ diff --git a/output-renderers/gds-output-renderer.c b/output-renderers/gds-output-renderer.c index 791d2dd..12aedf6 100644 --- a/output-renderers/gds-output-renderer.c +++ b/output-renderers/gds-output-renderer.c @@ -20,9 +20,6 @@ /** * @file gds-output-renderer.c * @brief Base GObject class for output renderers - * - * All output renderers are derived from this class - * * @author Mario Hüttel */ @@ -38,13 +35,20 @@ struct renderer_params { double scale; }; +struct idle_function_params { + GMutex message_lock; + char *status_message; +}; + typedef struct { gchar *output_file; LayerSettings *layer_settings; GMutex settings_lock; gboolean mutex_init_status; GTask *task; + GMainContext *main_context; struct renderer_params async_params; + struct idle_function_params idle_function_parameters; gpointer padding[11]; } GdsOutputRendererPrivate; @@ -56,7 +60,7 @@ enum { G_DEFINE_TYPE_WITH_PRIVATE(GdsOutputRenderer, gds_output_renderer, G_TYPE_OBJECT) -enum gds_output_renderer_signal_ids {ASYNC_FINISHED = 0, ASYNC_PROGRESS_UPDATE, GDS_OUTPUT_RENDERER_SIGNAL_COUNT}; +enum gds_output_renderer_signal_ids {ASYNC_FINISHED = 0, ASYNC_PROGRESS_CHANGED, GDS_OUTPUT_RENDERER_SIGNAL_COUNT}; static guint gds_output_renderer_signals[GDS_OUTPUT_RENDERER_SIGNAL_COUNT]; static int gds_output_renderer_render_dummy(GdsOutputRenderer *renderer, @@ -83,6 +87,11 @@ static void gds_output_renderer_dispose(GObject *self_obj) g_mutex_lock(&priv->settings_lock); g_mutex_unlock(&priv->settings_lock); g_mutex_clear(&priv->settings_lock); + + g_mutex_lock(&priv->idle_function_parameters.message_lock); + g_mutex_unlock(&priv->idle_function_parameters.message_lock); + g_mutex_clear(&priv->idle_function_parameters.message_lock); + priv->mutex_init_status = FALSE; } @@ -150,6 +159,7 @@ static GParamSpec *gds_output_renderer_properties[N_PROPERTIES] = {NULL}; static void gds_output_renderer_class_init(GdsOutputRendererClass *klass) { GObjectClass *oclass = G_OBJECT_CLASS(klass); + GType progress_changed_param_types[1] = {G_TYPE_POINTER}; klass->render_output = gds_output_renderer_render_dummy; @@ -178,16 +188,16 @@ static void gds_output_renderer_class_init(GdsOutputRendererClass *klass) G_TYPE_NONE, 0, NULL); - gds_output_renderer_signals[ASYNC_PROGRESS_UPDATE] = - g_signal_newv("progress-update", GDS_RENDER_TYPE_OUTPUT_RENDERER, + gds_output_renderer_signals[ASYNC_PROGRESS_CHANGED] = + g_signal_newv("progress-changed", GDS_RENDER_TYPE_OUTPUT_RENDERER, G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, NULL, NULL, NULL, NULL, G_TYPE_NONE, - 0, - NULL); + 1, + progress_changed_param_types); } void gds_output_renderer_init(GdsOutputRenderer *self) @@ -200,7 +210,10 @@ void gds_output_renderer_init(GdsOutputRenderer *self) priv->output_file = NULL; priv->task = NULL; priv->mutex_init_status = TRUE; + priv->main_context = NULL; + priv->idle_function_parameters.status_message = NULL; g_mutex_init(&priv->settings_lock); + g_mutex_init(&priv->idle_function_parameters.message_lock); return; } @@ -321,9 +334,7 @@ static void gds_output_renderer_async_wrapper(GTask *task, goto ret_from_task; } - g_mutex_lock(&priv->settings_lock); ret = gds_output_renderer_render_output(renderer, priv->async_params.cell, priv->async_params.scale); - g_mutex_unlock(&priv->settings_lock); ret_from_task: g_task_return_int(task, ret); @@ -337,8 +348,13 @@ static void gds_output_renderer_async_finished(GObject *src_obj, GAsyncResult *r priv = gds_output_renderer_get_instance_private(GDS_RENDER_OUTPUT_RENDERER(src_obj)); + priv->main_context = NULL; + g_signal_emit(src_obj, gds_output_renderer_signals[ASYNC_FINISHED], 0); g_clear_object(&priv->task); + + /* Clear reference set in gds_output_renderer_render_output_async() */ + g_object_unref(src_obj); } int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct gds_cell *cell, double scale) @@ -353,14 +369,82 @@ int gds_output_renderer_render_output_async(GdsOutputRenderer *renderer, struct } priv->task = g_task_new(renderer, NULL, gds_output_renderer_async_finished, NULL); + g_task_set_name(priv->task, "Rendering Thread"); + g_mutex_lock(&priv->settings_lock); priv->async_params.cell = cell; priv->async_params.scale = scale; + priv->main_context = g_main_context_default(); g_mutex_unlock(&priv->settings_lock); + /* Self reference. This could end up being nasty... */ + g_object_ref(renderer); + + /* Do the magic */ g_task_run_in_thread(priv->task, gds_output_renderer_async_wrapper); return ret; } +static gboolean idle_event_processor_callback(gpointer user_data) +{ + GdsOutputRenderer *renderer; + GdsOutputRendererPrivate *priv; + char *status_message; + + /* If the rendering is finished before the mainloop gets to this point + * the renderer is already disposed. Catch this! + */ + if (!GDS_RENDER_IS_OUTPUT_RENDERER(user_data)) + return FALSE; + + renderer = GDS_RENDER_OUTPUT_RENDERER(user_data); + priv = gds_output_renderer_get_instance_private(renderer); + + if (g_mutex_trylock(&priv->idle_function_parameters.message_lock)) { + status_message = priv->idle_function_parameters.status_message; + g_signal_emit(renderer, gds_output_renderer_signals[ASYNC_PROGRESS_CHANGED], 0, status_message); + g_free(priv->idle_function_parameters.status_message); + priv->idle_function_parameters.status_message = NULL; + g_mutex_unlock(&priv->idle_function_parameters.message_lock); + } else { + return TRUE; + } + + return FALSE; +} + +void gds_output_renderer_update_gui_status_from_async(GdsOutputRenderer *renderer, const char *status) +{ + GSource *idle_event_processor; + GdsOutputRendererPrivate *priv; + gboolean skip_source = FALSE; + + g_return_if_fail(GDS_RENDER_IS_OUTPUT_RENDERER(renderer)); + if (!status) + return; + + priv = gds_output_renderer_get_instance_private(renderer); + + /* If rendering is not async */ + if (!priv->main_context) + return; + + g_mutex_lock(&priv->idle_function_parameters.message_lock); + if (priv->idle_function_parameters.status_message) { + g_free(priv->idle_function_parameters.status_message); + + /* Skip adding new idle source because there's already an active one */ + skip_source = TRUE; + } + priv->idle_function_parameters.status_message = g_strdup(status); + g_mutex_unlock(&priv->idle_function_parameters.message_lock); + + if (!skip_source) { + idle_event_processor = g_idle_source_new(); + g_source_set_callback(idle_event_processor, idle_event_processor_callback, (gpointer)renderer, NULL); + g_source_attach(idle_event_processor, priv->main_context); + } +} + /** @} */ diff --git a/output-renderers/latex-renderer.c b/output-renderers/latex-renderer.c index de1113d..e0b21d6 100644 --- a/output-renderers/latex-renderer.c +++ b/output-renderers/latex-renderer.c @@ -29,10 +29,15 @@ #include #include /** - * @addtogroup LatexRenderer + * @addtogroup LaTeX-Renderer * @{ */ +/** + * @brief Struct representing the LaTeX-Renderer object. + * + * This struct holds the LaTeX renderer internal data. It is only used inside the @ref LatexRenderer class. + */ struct _LatexRenderer { GdsOutputRenderer parent; gboolean tex_standalone; @@ -226,13 +231,20 @@ static void generate_graphics(FILE *tex_file, GList *graphics, GList *linfo, GSt * @param tex_file File to write to * @param buffer Working buffer * @param scale Scale output down by this value + * @param renderer The current renderer as GdsOutputRenderer. This is used to emit the status updates to the GUI */ -static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, GString *buffer, double scale) +static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, GString *buffer, double scale, + GdsOutputRenderer *renderer) { - + GString *status; GList *list_child; struct gds_cell_instance *inst; + status = g_string_new(NULL); + g_string_printf(status, "Generating cell %s", cell->name); + gds_output_renderer_update_gui_status_from_async(renderer, status->str); + g_string_free(status, TRUE); + /* Draw polygons of current cell */ generate_graphics(tex_file, cell->graphic_objs, layer_infos, buffer, scale); @@ -256,7 +268,7 @@ static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_fil inst->magnification); WRITEOUT_BUFFER(buffer); - render_cell(inst->cell_ref, layer_infos, tex_file, buffer, scale); + render_cell(inst->cell_ref, layer_infos, tex_file, buffer, scale, renderer); g_string_printf(buffer, "\\end{scope}\n"); WRITEOUT_BUFFER(buffer); @@ -271,7 +283,7 @@ static void render_cell(struct gds_cell *cell, GList *layer_infos, FILE *tex_fil } static int latex_render_cell_to_code(struct gds_cell *cell, GList *layer_infos, FILE *tex_file, double scale, - gboolean create_pdf_layers, gboolean standalone_document) + gboolean create_pdf_layers, gboolean standalone_document, GdsOutputRenderer *renderer) { GString *working_line; @@ -304,7 +316,7 @@ static int latex_render_cell_to_code(struct gds_cell *cell, GList *layer_infos, WRITEOUT_BUFFER(working_line); /* Generate graphics output */ - render_cell(cell, layer_infos, tex_file, working_line, scale); + render_cell(cell, layer_infos, tex_file, working_line, scale, renderer); g_string_printf(working_line, "\\end{tikzpicture}\n"); @@ -344,7 +356,7 @@ static int latex_renderer_render_output(GdsOutputRenderer *renderer, tex_file = fopen(output_file, "w"); if (tex_file) { ret = latex_render_cell_to_code(cell, layer_infos, tex_file, scale, - l_renderer->pdf_layers, l_renderer->tex_standalone); + l_renderer->pdf_layers, l_renderer->tex_standalone, renderer); fclose(tex_file); } else { g_error("Could not open LaTeX output file"); diff --git a/widgets/activity-bar.c b/widgets/activity-bar.c index 94e2474..dccd9d0 100644 --- a/widgets/activity-bar.c +++ b/widgets/activity-bar.c @@ -105,5 +105,11 @@ void activity_bar_set_ready(ActivityBar *bar) gtk_spinner_stop(GTK_SPINNER(bar->spinner)); } +void activity_bar_set_busy(ActivityBar *bar, const char *text) +{ + gtk_label_set_text(GTK_LABEL(bar->label), (text ? text : "Working...")); + gtk_spinner_start(GTK_SPINNER(bar->spinner)); +} + /** @} */