static int executeJump(void) { /* * 0 = No jump pending, nothing done. * 1 = Jump succeeded. * 2 = Jump was pending, but target page invalid. */ int retval = 0; /* Jump? */ if (target_page >= 0) { target_page--; /* Restrict to valid range. */ if (target_page >= 0 && target_page < doc_n_pages) { doc_page = target_page; doc_page_beamer = target_page; setStatusText_strdup("Ready."); retval = 1; } else { setStatusText_strdup("Invalid page."); retval = 2; } /* Reset value to: "no jump pending". */ target_page = -1; } return retval; }
static int executeJump(void) { /* * 0 = No jump pending, nothing done. * 1 = Jump succeeded. * 2 = Jump was pending, but target page invalid. */ int retval = 0; /* Jump? */ if (target_page >= 0) { int label = 0; int runs = 0; int page_number = target_page-1; while (runs < doc_n_pages && page_number >= 0 && page_number < doc_n_pages) { PopplerPage *page = poppler_document_get_page(doc, page_number); label = atoi(poppler_page_get_label(page)); if (label == target_page) break; if (label < target_page) page_number++; else if (label > target_page) page_number--; runs++; g_object_unref(G_OBJECT(page)); } target_page = page_number; /* Restrict to valid range. */ if (target_page >= 0 && target_page < doc_n_pages) { doc_page = target_page; doc_page_beamer = target_page; setStatusText_strdup("Ready."); retval = 1; } else { setStatusText_strdup("Invalid page."); retval = 2; } /* Reset value to: "no jump pending". */ target_page = -1; } return retval; }
static void onSaveAsClicked(GtkWidget *widget, gpointer data) { /* Unused parameters. */ (void)widget; (void)data; gchar *msg = NULL; GtkWidget *fileChooser = NULL; fileChooser = gtk_file_chooser_dialog_new("Save File", NULL, GTK_FILE_CHOOSER_ACTION_SAVE, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_SAVE, GTK_RESPONSE_ACCEPT, NULL); gtk_file_chooser_set_do_overwrite_confirmation( GTK_FILE_CHOOSER(fileChooser), TRUE); setLastFolderOn(fileChooser); if (gtk_dialog_run(GTK_DIALOG(fileChooser)) == GTK_RESPONSE_ACCEPT) { saveCurrentNote(); if (savedAsFilename != NULL) g_free(savedAsFilename); savedAsFilename = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(fileChooser)); if (saveNotes(savedAsFilename)) { saveLastFolderFrom(fileChooser); isSaved = TRUE; gtk_widget_set_sensitive(GTK_WIDGET(saveButton), FALSE); msg = g_strdup_printf("Notes saved as '%s'.", savedAsFilename); setStatusText_strdup(msg); g_free(msg); } } gtk_widget_destroy(fileChooser); }
static void showNotesFromFile(gchar *notefile) { gchar *msg = NULL; if (savedAsFilename != NULL) g_free(savedAsFilename); savedAsFilename = notefile; if (readNotes(notefile)) { msg = g_strdup_printf("Notes read from '%s'.", notefile); setStatusText_strdup(msg); g_free(msg); isSaved = TRUE; gtk_widget_set_sensitive(GTK_WIDGET(saveButton), FALSE); printNote(doc_page + 1); } }
static void onSaveClicked(GtkWidget *widget, gpointer data) { /* Unused parameters. */ (void)widget; (void)data; gchar *msg = NULL; if (savedAsFilename == NULL) return; saveCurrentNote(); if (!saveNotes(savedAsFilename)) return; msg = g_strdup_printf("Notes saved: '%s'.", savedAsFilename); setStatusText_strdup(msg); g_free(msg); isSaved = TRUE; gtk_widget_set_sensitive(GTK_WIDGET(saveButton), FALSE); }
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); } }
static gboolean onKeyPressed(GtkWidget *widget, GdkEventKey *ev, gpointer data) { /* Unused parameters. */ (void)data; guint key = ev->keyval; gchar *msg = NULL; /* When inside the note pad, don't do anything here. */ if (isInsideNotePad) return FALSE; /* Jump command? * * Note: This works as long as the values of GDK keysyms satisfy: * 1) GDK_0 < GDK_1 < GDK_2 < ... < GDK_9 * 2) All of them must be >= 0. */ key -= GDK_0; if (key <= 9) { /* The initial value is -1, so we have to reset this on the * first key stroke. */ if (target_page < 0) target_page = 0; /* Do a "decimal left shift" and add the given value. */ target_page *= 10; target_page += (int)key; /* Catch overflow and announce what would happen. */ if (target_page < 0) { target_page = -1; setStatusText_strdup("Invalid page."); } else { msg = g_strdup_printf("Jump to page: %d", target_page); setStatusText_strdup(msg); g_free(msg); } return FALSE; } gboolean changed = TRUE; saveCurrentNote(); switch (ev->keyval) { case GDK_Right: case GDK_Down: case GDK_Page_Down: case GDK_space: nextSlide(); break; case GDK_Left: case GDK_Up: case GDK_Page_Up: prevSlide(); break; case GDK_F5: /* Switch to fullscreen (if needed) and start the timer * (unless it's already running). */ if (!isFullScreen) toggleFullScreen(); if (timerMode != 1) toggleTimer(); break; case GDK_F6: /* this shall trigger a hard refresh, so empty the cache. */ clearCache(); break; case GDK_w: runpref.fit_mode = FIT_WIDTH; break; case GDK_h: runpref.fit_mode = FIT_HEIGHT; break; case GDK_p: runpref.fit_mode = FIT_PAGE; break; case GDK_l: current_fixate(); break; case GDK_L: current_release(FALSE); break; case GDK_J: current_release(TRUE); break; case GDK_f: toggleFullScreen(); break; case GDK_s: toggleTimer(); changed = FALSE; break; case GDK_c: toggleCurserVisibility(); break; case GDK_r: resetTimer(); changed = FALSE; break; case GDK_Escape: case GDK_q: if (prefs.q_exits_fullscreen && isFullScreen) { toggleFullScreen(); if (prefs.stop_timer_on_fs) { toggleTimer(); } } else { changed = FALSE; onQuit(NULL, NULL, NULL); } break; case GDK_i: /* This must not work when we're on the beamer window. */ if (widget != win_beamer) setEditingState(TRUE); changed = FALSE; break; case GDK_Return: if (executeJump() == 0) nextSlide(); break; case GDK_G: executeJump(); break; case GDK_period: case GDK_b: toggleBlankBeamer(); changed = FALSE; break; default: changed = FALSE; } if (changed == TRUE) { refreshPorts(); } return TRUE; }