static void refreshPorts(void) { struct viewport *pp = NULL; GList *it = ports; /* display. */ while (it) { pp = (struct viewport *)(it->data); updatePortPixbuf(pp); it = g_list_next(it); } refreshFrames(); /* queue prerendering of next slides unless this has already been * done. * * note: usually, it's not safe to use booleans for purposes like * this. however, we're in a singlethreaded program, so no other * thread can do concurrent modifications to this variable. hence * it's okay. */ if (!preQueued) { preQueued = TRUE; g_idle_add(idleFillCaches, NULL); } }
static void refreshPorts(void) { struct viewport *pp = NULL; GList *it = ports; /* Queue a draw signal for all ports. */ while (it) { pp = (struct viewport *)(it->data); gtk_widget_queue_draw(pp->canvas); it = g_list_next(it); } refreshFrames(); }
static void initGUI(int numframes, gchar *notefile) { int i = 0, transIndex = 0; GtkWidget *timeBox = NULL, *notePadBox = NULL, *notePadScroll = NULL, *table = NULL; GtkWidget *image = NULL, *frame = NULL, *evbox = NULL, *outerevbox = NULL, *timeFrame = NULL; GtkWidget *mainVBox = NULL; GdkColor black; GtkWidget *toolbar = NULL, *timeToolbar = NULL; GtkToolItem *openButton = NULL, *saveAsButton = NULL, *fontSelectButton = NULL, *timeFontSelectButton = NULL; PangoFontDescription *font_desc = NULL; struct viewport *thisport = NULL; /* init colors */ if (gdk_color_parse("#000000", &black) != TRUE) fprintf(stderr, "Could not resolve color \"black\".\n"); if (gdk_color_parse("#BBFFBB", &col_current) != TRUE) fprintf(stderr, "Could not resolve color \"col_current\".\n"); if (gdk_color_parse("#FFBBBB", &col_marked) != TRUE) fprintf(stderr, "Could not resolve color \"col_marked\".\n"); if (gdk_color_parse("#BBBBBB", &col_dim) != TRUE) fprintf(stderr, "Could not resolve color \"col_dim\".\n"); /* init our two windows */ win_preview = gtk_window_new(GTK_WINDOW_TOPLEVEL); win_beamer = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(win_preview), "pdfpres - Preview"); gtk_window_set_title(GTK_WINDOW(win_beamer), "pdfpres - Beamer"); g_signal_connect(G_OBJECT(win_preview), "delete_event", G_CALLBACK(onQuit), NULL); g_signal_connect(G_OBJECT(win_preview), "destroy", G_CALLBACK(onQuit), NULL); g_signal_connect(G_OBJECT(win_beamer), "delete_event", G_CALLBACK(onQuit), NULL); g_signal_connect(G_OBJECT(win_beamer), "destroy", G_CALLBACK(onQuit), NULL); g_signal_connect(G_OBJECT(win_preview), "key_press_event", G_CALLBACK(onKeyPressed), NULL); g_signal_connect(G_OBJECT(win_beamer), "key_press_event", G_CALLBACK(onKeyPressed), NULL); gtk_widget_add_events(win_beamer, GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK); g_signal_connect(G_OBJECT(win_beamer), "button_release_event", G_CALLBACK(onMouseReleased), NULL); g_signal_connect(G_OBJECT(win_beamer), "scroll_event", G_CALLBACK(onScroll), NULL); gtk_widget_add_events(win_preview, GDK_BUTTON_RELEASE_MASK | GDK_SCROLL_MASK); g_signal_connect(G_OBJECT(win_preview), "button_release_event", G_CALLBACK(onMouseReleased), NULL); g_signal_connect(G_OBJECT(win_preview), "scroll_event", G_CALLBACK(onScroll), NULL); gtk_container_set_border_width(GTK_CONTAINER(win_preview), 0); gtk_container_set_border_width(GTK_CONTAINER(win_beamer), 0); gtk_widget_modify_bg(win_beamer, GTK_STATE_NORMAL, &black); /* create buttons */ timeToolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(timeToolbar), GTK_TOOLBAR_ICONS); gtk_container_set_border_width(GTK_CONTAINER(timeToolbar), 5); if (!prefs.timer_is_clock) { startButton = gtk_tool_button_new_from_stock( GTK_STOCK_MEDIA_PLAY); g_signal_connect(G_OBJECT(startButton), "clicked", G_CALLBACK(toggleTimer), NULL); gtk_toolbar_insert(GTK_TOOLBAR(timeToolbar), startButton, -1); resetButton = gtk_tool_button_new_from_stock(GTK_STOCK_MEDIA_REWIND); g_signal_connect(G_OBJECT(resetButton), "clicked", G_CALLBACK(resetTimer), NULL); gtk_toolbar_insert(GTK_TOOLBAR(timeToolbar), resetButton, -1); gtk_toolbar_insert(GTK_TOOLBAR(timeToolbar), gtk_separator_tool_item_new(), -1); } timeFontSelectButton = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_FONT); gtk_toolbar_insert(GTK_TOOLBAR(timeToolbar), timeFontSelectButton, -1); g_signal_connect(G_OBJECT(timeFontSelectButton), "clicked", G_CALLBACK(onTimerFontSelectClick), NULL); /* setting text size for time label */ timeElapsedLabel = gtk_label_new(NULL); font_desc = pango_font_description_from_string(prefs.font_timer); gtk_widget_modify_font(GTK_WIDGET(timeElapsedLabel), font_desc); pango_font_description_free(font_desc); if (prefs.timer_is_clock) { printCurrentTime(timeElapsedLabel); } else { gtk_label_set_text(GTK_LABEL(timeElapsedLabel), "00:00"); } /* Add timer label to another event box so we can set a nice border. */ evbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(evbox), timeElapsedLabel); gtk_container_set_border_width(GTK_CONTAINER(evbox), 10); /* create timer */ timeBox = gtk_vbox_new(FALSE, 5); gtk_box_pack_start(GTK_BOX(timeBox), evbox, TRUE, TRUE, 5); gtk_box_pack_start(GTK_BOX(timeBox), timeToolbar, FALSE, FALSE, 5); if (prefs.timer_is_clock) { timeFrame = gtk_frame_new("Clock"); } else { timeFrame = gtk_frame_new("Timer"); } gtk_container_add(GTK_CONTAINER(timeFrame), timeBox); /* create note pad inside a scrolled window */ notePadBox = gtk_vbox_new(FALSE, 2); notePadScroll = gtk_scrolled_window_new(NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(notePadScroll), 5); notePadFrame = gtk_frame_new("Notes for current slide"); notePad = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW(notePad), FALSE); gtk_text_view_set_cursor_visible(GTK_TEXT_VIEW(notePad), FALSE); g_signal_connect(G_OBJECT(notePad), "key_press_event", G_CALLBACK(onPadKeyPressed), NULL); /* Remarks: * * - The note pad uses word wrapping. If that's not enough, it also * uses wrapping on a per character basis. * - The note pad is placed into a GtkScrolledWindow. This window * allows vertical scrolling but no horizontal scrolling. */ gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(notePad), GTK_WRAP_WORD_CHAR); gtk_container_add(GTK_CONTAINER(notePadScroll), notePad); gtk_scrolled_window_set_shadow_type( GTK_SCROLLED_WINDOW(notePadScroll), GTK_SHADOW_IN); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(notePadScroll), GTK_POLICY_NEVER, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(notePadBox), notePadScroll, TRUE, TRUE, 2); /* set note pad font and margin */ font_desc = pango_font_description_from_string(prefs.font_notes); gtk_widget_modify_font(notePad, font_desc); pango_font_description_free(font_desc); gtk_text_view_set_left_margin(GTK_TEXT_VIEW(notePad), 5); gtk_text_view_set_right_margin(GTK_TEXT_VIEW(notePad), 5); noteBuffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(notePad)); /* We detect changes of the notes by catching the "changed" signal. * As this signal is also emitted when we change the buffer * programmatically, we first have a look if there was a * "begin_user_action" signal. If so, the user has changed the * buffer. */ g_signal_connect(G_OBJECT(noteBuffer), "changed", G_CALLBACK(onEditing), NULL); g_signal_connect(G_OBJECT(noteBuffer), "begin_user_action", G_CALLBACK(onBeginUserAction), NULL); g_signal_connect(G_OBJECT(noteBuffer), "end_user_action", G_CALLBACK(onEndUserAction), NULL); /* create toolbar */ toolbar = gtk_toolbar_new(); gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_ICONS); gtk_container_set_border_width(GTK_CONTAINER(toolbar), 5); openButton = gtk_tool_button_new_from_stock(GTK_STOCK_OPEN); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), openButton, -1); g_signal_connect(G_OBJECT(openButton), "clicked", G_CALLBACK(onOpenClicked), NULL); /* TODO: Tooltips?! */ saveButton = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), saveButton, -1); gtk_widget_set_sensitive(GTK_WIDGET(saveButton), FALSE); g_signal_connect(G_OBJECT(saveButton), "clicked", G_CALLBACK(onSaveClicked), NULL); saveAsButton = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE_AS); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), saveAsButton, -1); g_signal_connect(G_OBJECT(saveAsButton), "clicked", G_CALLBACK(onSaveAsClicked), NULL); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), gtk_separator_tool_item_new(), -1); editButton = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_EDIT); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), editButton, -1); g_signal_connect(G_OBJECT(editButton), "toggled", G_CALLBACK(onEditToggled), NULL); fontSelectButton = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_FONT); gtk_toolbar_insert(GTK_TOOLBAR(toolbar), fontSelectButton, -1); g_signal_connect(G_OBJECT(fontSelectButton), "clicked", G_CALLBACK(onFontSelectClick), NULL); gtk_box_pack_start(GTK_BOX(notePadBox), toolbar, FALSE, FALSE, 2); gtk_container_add(GTK_CONTAINER(notePadFrame), notePadBox); /* init containers for "preview" */ table = gtk_table_new(numframes, numframes + 1, TRUE); gtk_table_set_col_spacings(GTK_TABLE(table), 5); gtk_container_set_border_width(GTK_CONTAINER(table), 10); /* dynamically create all the frames */ for (i = 0; i < numframes; i++) { /* calc the offset for this frame */ transIndex = i - (int)((double)numframes / 2.0); /* create the widget - note that it is important not to * set the title to NULL. this would cause a lot more * redraws on startup because the frame will get re- * allocated when the title changes. */ frame = gtk_frame_new(""); /* create a new drawing area - the pdf will be rendered in * there */ image = gtk_image_new(); /* add widgets to their parents. the image is placed in an * eventbox, the box's size_allocate signal will be handled. so, * we know the exact width/height we can render into. (placing * the image into the frame would create the need of knowing the * frame's border size...) */ evbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(evbox), image); gtk_container_add(GTK_CONTAINER(frame), evbox); /* every frame will be placed in another eventbox so we can set a * background color */ outerevbox = gtk_event_box_new(); gtk_container_add(GTK_CONTAINER(outerevbox), frame); if (i == 0) { gtk_table_attach_defaults(GTK_TABLE(table), notePadFrame, 0, 1, 0, numframes - 1); gtk_table_attach_defaults(GTK_TABLE(table), outerevbox, 0, 1, numframes - 1, numframes); } else { if (i == numframes - 1) { gtk_table_attach_defaults(GTK_TABLE(table), outerevbox, numframes, numframes + 1, 0, 1); gtk_table_attach_defaults(GTK_TABLE(table), timeFrame, numframes, numframes + 1, numframes - 1, numframes); } else { if (i == (int)(numframes / 2)) { gtk_table_attach_defaults(GTK_TABLE(table), outerevbox, i, i + 2, 0, numframes); } else { if (i < (int)(numframes / 2)) { gtk_table_attach_defaults(GTK_TABLE(table), outerevbox, i, i + 1, numframes - i - 1, numframes - i); } else { gtk_table_attach_defaults(GTK_TABLE(table), outerevbox, i + 1, i + 2, numframes - i - 1, numframes - i); } } } } /* make the eventbox "transparent" */ gtk_event_box_set_visible_window(GTK_EVENT_BOX(evbox), FALSE); /* save info of this rendering port */ thisport = (struct viewport *)malloc(sizeof(struct viewport)); g_assert(thisport); thisport->offset = transIndex; thisport->image = image; thisport->frame = frame; thisport->pixbuf = NULL; thisport->width = -1; thisport->height = -1; thisport->isBeamer = FALSE; ports = g_list_append(ports, thisport); /* resize callback */ g_signal_connect(G_OBJECT(evbox), "size_allocate", G_CALLBACK(onResize), thisport); g_signal_connect(G_OBJECT(evbox), "expose_event", G_CALLBACK(onExpose), thisport); } /* Add main content and a status bar to preview window. * * Note: It's important to use gtk_box_pack_* to add the statusbar * because gtk_container_add will pick unappropriate defaults. */ mainVBox = gtk_vbox_new(FALSE, 5); gtk_container_add(GTK_CONTAINER(mainVBox), table); mainStatusbar = gtk_statusbar_new(); gtk_box_pack_end(GTK_BOX(mainVBox), mainStatusbar, FALSE, FALSE, 0); setStatusText_strdup("Ready."); gtk_container_add(GTK_CONTAINER(win_preview), mainVBox); /* in order to set the initially highlighted frame */ refreshFrames(); /* add a rendering area to the beamer window */ image = gtk_image_new(); gtk_container_add(GTK_CONTAINER(win_beamer), image); /* save info of this rendering port */ thisport = (struct viewport *)malloc(sizeof(struct viewport)); g_assert(thisport); thisport->offset = 0; thisport->image = image; thisport->frame = NULL; thisport->pixbuf = NULL; thisport->width = -1; thisport->height = -1; thisport->isBeamer = TRUE; ports = g_list_append(ports, thisport); /* connect the on-resize-callback directly to the window */ g_signal_connect(G_OBJECT(win_beamer), "size_allocate", G_CALLBACK(onResize), thisport); g_signal_connect(G_OBJECT(win_beamer), "expose_event", G_CALLBACK(onExpose), thisport); /* load notes if requested */ if (notefile) { showNotesFromFile(notefile); } /* Set default sizes for both windows. (Note: If the widgets don't * fit into that space, the windows will be larger. Also, they are * allowed to get shrinked by the user.) */ gtk_window_set_default_size(GTK_WINDOW(win_preview), 640, 480); gtk_window_set_default_size(GTK_WINDOW(win_beamer), 320, 240); /* show the windows */ gtk_widget_show_all(win_preview); gtk_widget_show_all(win_beamer); /* now, as the real gdk window exists, hide mouse cursor in the * beamer window */ gdk_window_set_cursor(gtk_widget_get_window(GTK_WIDGET(win_beamer)), gdk_cursor_new(GDK_BLANK_CURSOR)); /* Show a clock or a timer? */ if (prefs.timer_is_clock) { g_timeout_add(500, (GSourceFunc)printCurrentTime, (gpointer)timeElapsedLabel); } else { g_timeout_add(500, (GSourceFunc)printTimeElapsed, (gpointer)timeElapsedLabel); } }