notepad_window.cpp

Go to the documentation of this file.
00001 /*
00002  * File Name: notepad_window.cpp
00003  */
00004 
00005 /*
00006  * This file is part of notepad.
00007  *
00008  * notepad is free software: you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation, either version 2 of the License, or
00011  * (at your option) any later version.
00012  *
00013  * notepad is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00020  */
00021 
00022 /**
00023  * Copyright (C) 2010 IREX Technologies B.V.
00024  * All rights reserved.
00025  */
00026 
00027 #include <cstring>
00028 #include <glib.h>
00029 #include <gdk/gdkkeysyms.h>
00030 #include "notepad_rename.h"
00031 #include <libergtk/erGtkDialogJumpToPage.h>
00032 
00033 #include "log.h"
00034 #include "i18n.h"
00035 #include "notepad_display_sched.h"
00036 #include "notepad_ipc.h"
00037 #include "notepad_doc.h"
00038 #include "notepad_pages.h"
00039 #include "notepad_window.h"
00040 #include "notepad_point.h"
00041 #include "notepad_thumbnail.h"
00042 
00043 namespace notepad 
00044 {
00045 
00046     static const char ILLEGAL_FILENAME_CHARS[] = "/\\:*?<>|"; // forbidden filename characthers
00047 
00048     CNotepadWindow::CNotepadWindow()
00049         : main_window(0)
00050         , source(this)
00051         , notepad_doc()
00052         , notepad_pages()
00053         , gctx()
00054         , current_image(0)
00055         , erscribble_mode(TRUE)
00056         , erscribble_status(-1)
00057         , erase_status(-1)
00058         , current_page(1) 
00059         , invert_pageturn(FALSE)
00060         , skip_expose_events(FALSE)
00061         , doublepagefliptimout(0)
00062     {
00063 
00064     }
00065 
00066     bool CNotepadWindow::init(bool docreate, const gchar* filename)
00067     {
00068         //document
00069         np_result res = NP_OK;
00070         if (docreate)
00071         {
00072             res = notepad_doc.create(filename);
00073         }
00074         else
00075         {
00076             res = notepad_doc.open(filename);
00077         }
00078 
00079         if (res != NP_OK) 
00080         {
00081             menu_block(TRUE);
00082             GtkWidget* openfaildialog = gtk_message_dialog_new(
00083                     GTK_WINDOW(main_window), 
00084                     GTK_DIALOG_MODAL,
00085                     GTK_MESSAGE_WARNING, 
00086                     GTK_BUTTONS_OK,
00087                     (docreate) ? 
00088                         _("A new note cannot be created at this time. \n"
00089                           "It is possible that the memory card is full. "
00090                           "Consider deleting some items and then try again.")
00091                         : 
00092                         _("The note could not be opened. \n"
00093                           "It is possible that the file has been corrupted."));
00094 
00095             gtk_dialog_run(GTK_DIALOG(openfaildialog));
00096             menu_block(FALSE);
00097             gtk_widget_destroy(openfaildialog);
00098 
00099             return false;
00100         }
00101 
00102         //window
00103         main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
00104         connect_event_handlers(main_window);
00105         g_signal_connect(main_window, "destroy", G_CALLBACK(gtk_widget_destroyed), &main_window);
00106         gtk_window_maximize(GTK_WINDOW(main_window));
00107 
00108         //page
00109         if ( notepad_doc.get_num_pages() < 1 )
00110         {
00111             notepad_doc.insert_page(current_page);
00112         }
00113         else
00114         {
00115             int num = notepad_doc.get_last_read();
00116             if (num > 0 )
00117             {
00118                 current_page = num;
00119             }
00120         }
00121 
00122         // initialize pages cache
00123         gtk_widget_realize(main_window); // need gdk window now for depth/visual
00124         GdkVisual* visual = gdk_drawable_get_visual(GDK_DRAWABLE(main_window->window));
00125         notepad_pages.init(&notepad_doc, &gctx, current_page, visual); 
00126 
00127         // create gdk graphical context
00128         if (gctx.gc == 0)
00129         {
00130            gctx.gc = gdk_gc_new(GDK_DRAWABLE(main_window->window));
00131         }
00132 
00133 
00134         return true;
00135     }
00136 
00137     CNotepadWindow::~CNotepadWindow()
00138     {
00139         notepad_doc.set_last_read(current_page); // store the current page number
00140         notepad_doc.close(); // close scribble
00141         disconnect_event_handlers(main_window); // unref GDK events
00142         notepad_pages.stop();
00143 
00144         if (main_window)
00145         {
00146             gtk_widget_destroy(main_window);
00147             main_window = 0;
00148         }
00149         if (gctx.gc != 0)
00150         {
00151             g_object_unref(G_OBJECT(gctx.gc));
00152             gctx.gc = 0;
00153         }
00154     }
00155 
00156     void CNotepadWindow::quit()
00157     {
00158         LOGPRINTF("entry");
00159 
00160         CThumbnail saveThumbnail(*notepad_doc.getFileStore(), notepad_pages, gctx.margin); // store thumbnail
00161 
00162         notepad_pages.stop();
00163         gtk_main_quit();
00164     }
00165 
00166     GtkWidget* CNotepadWindow::getWindow()
00167     {
00168         return main_window;
00169     }
00170 
00171     gchar* CNotepadWindow::getLabel()
00172     {
00173         CFileStore* store = notepad_doc.getFileStore();
00174         return store->getFileNameFilePartNoExt();
00175     }
00176 
00177     const gchar* CNotepadWindow::getFilename()
00178     {
00179         CFileStore* store = notepad_doc.getFileStore();
00180         return store->getFilename();
00181     }
00182 
00183 
00184     void CNotepadWindow::show(bool show)
00185     {
00186         LOGPRINTF("entry");
00187         if (show)
00188             gtk_widget_show(main_window);
00189         else
00190             gtk_widget_hide(main_window);
00191         LOGPRINTF("exit");
00192     }
00193 
00194     void CNotepadWindow::connect_event_handlers(GtkWidget *widget)
00195     {
00196         // Install event handlers by using member functions.
00197         source.connect_event_handlers(widget);
00198         source.set_event_handler(EventSource::EVENT_EXPOSE,
00199                 &CNotepadWindow::on_expose);
00200         source.set_event_handler(EventSource::EVENT_VISIBILITY_NOTIFY,
00201                 &CNotepadWindow::on_visibility_notify);
00202         source.set_event_handler(EventSource::EVENT_CONFIG,
00203                 &CNotepadWindow::on_configure);
00204         source.set_event_handler(EventSource::EVENT_BUTTON_PRESS,
00205                 &CNotepadWindow::on_button_press);
00206         source.set_event_handler(EventSource::EVENT_BUTTON_RELEASE,
00207                 &CNotepadWindow::on_button_release);
00208         source.set_event_handler(EventSource::EVENT_MOTION_NOTIFY,
00209                 &CNotepadWindow::on_motion_notify);
00210         source.set_event_handler(EventSource::EVENT_KEY_PRESS,
00211                 &CNotepadWindow::on_key_press);
00212         source.set_event_handler(EventSource::EVENT_KEY_RELEASE,
00213                 &CNotepadWindow::on_key_release);
00214         source.set_event_handler(EventSource::EVENT_DELETE,
00215                 &CNotepadWindow::on_delete);
00216     }
00217 
00218     void CNotepadWindow::on_configure(GdkEvent* event)
00219     {
00220         LOGPRINTF("entry");
00221 
00222         // At first we need to update all widget related stuffs.
00223         GdkEventConfigure *cfg_event = reinterpret_cast<GdkEventConfigure *>(event);
00224 
00225         // Ignore 2nd configure event while rotating
00226         // Recognized if send_event is true
00227         if (cfg_event->send_event == TRUE)
00228         {
00229             LOGPRINTF("exit, skipped, send_event == true");
00230             return;
00231         }
00232 
00233         // Check if size has changed
00234         if (  (gctx.width  == cfg_event->width )
00235            && (gctx.height == cfg_event->height) )
00236         {
00237             LOGPRINTF("exit, size not changed, skip");
00238             return;
00239         }
00240 
00241         // set_rotation
00242         set_rotation( ipc_get_rotation() );
00243 
00244         LOGPRINTF("exit");
00245     }
00246 
00247     void CNotepadWindow::on_expose(GdkEvent* event)
00248     {
00249         LOGPRINTF("entry");
00250         static bool blockedstate = false;
00251 
00252         // dont upate while being rotated
00253         if (skip_expose_events)
00254         {
00255             blockedstate = true;
00256             LOGPRINTF("exit, skipped");
00257             return;
00258         }
00259         if (blockedstate)
00260         {
00261             blockedstate = false;
00262         }
00263         if (gctx.gc != 0)
00264         {
00265             update(); // refresh screen
00266         }
00267 
00268         LOGPRINTF("exit");
00269     }
00270 
00271     void CNotepadWindow::on_visibility_notify(GdkEvent* event)
00272     {
00273         // Block expose events while rotating the x-server.
00274         // Rotating begins with a GDK_VISIBILITY_FULLY_OBSCURED event
00275         // and ends with a GDK_VISIBILITY_UNOBSCURED event
00276 
00277         GdkEventVisibility* v_event = reinterpret_cast<GdkEventVisibility *>(event);
00278         LOGPRINTF("%d", v_event->state);
00279         if (!skip_expose_events) 
00280         {
00281             if (v_event->state == GDK_VISIBILITY_FULLY_OBSCURED)
00282             {
00283                 skip_expose_events = true;
00284                 LOGPRINTF("GDK_VISIBILITY_FULLY_OBSCURED, skip_expose_events");
00285             }
00286             else
00287             {
00288                 // Refresh screen now. 
00289                 // NOTE: This is important: otherwise the strokes become invisible when 
00290                 // the popupmenu appears. Because they are drawn on the image and the 
00291                 // delta driver but not on the window. 
00292                 LOGPRINTF("quick refresh screen");
00293                 draw_image_to_window();
00294             }
00295         }
00296         else 
00297         {
00298             if (v_event->state == GDK_VISIBILITY_UNOBSCURED)
00299             {
00300                 skip_expose_events = false;
00301                 LOGPRINTF("GDK_VISIBILITY_UNOBSCURED, pass expose_events and invalidate whole window");
00302                 // invalidate whole area, not only part of, important while rotating
00303                 GdkRectangle rect = {0,0, gctx.width, gctx.height};
00304                 gdk_window_invalidate_rect(main_window->window, &rect, FALSE); 
00305             }
00306         }
00307     }
00308 
00309     void CNotepadWindow::set_rotation(np_rotation rotation)
00310     {
00311         // drawn for the first time, or being resized/rotated: set dimensions, create new pixmap and draw 
00312         LOGPRINTF("entry");
00313 
00314         if (gctx.width != 0 && gctx.height != 0)
00315         {
00316             // only in initialized state, that is: after first set_rotation()
00317             (void) save(); // save current page 
00318         }
00319 
00320         if (rotation != gctx.rotation)
00321         {
00322             LOGPRINTF("change rotation, erase page cache");
00323             notepad_pages.mark_all_dirty(); // all cached pages are invalid because of rotation
00324         }
00325 
00326         // inner border
00327         int width, height;
00328         gtk_window_get_size(GTK_WINDOW(main_window), &width, &height);
00329 
00330         // root border
00331         int root_width, root_height;
00332         gdk_window_get_geometry(gdk_get_default_root_window(), NULL, NULL, &root_width, &root_height, NULL);
00333 
00334         int posx = 0, posy = 0;
00335         gdk_window_get_position(main_window->window, &posx, &posy);
00336 
00337         gctx.rotation  = rotation; 
00338         gctx.width     = width;
00339         gctx.height    = height;
00340 
00341         int my_border = root_height - height;
00342         gctx.margin = my_border;
00343 
00344         CNPoint::setRotation(rotation);
00345         CNPoint::setScreen(root_width, root_height);
00346         CNPoint::setClipBorder(my_border);
00347 
00348         LOGPRINTF("gdk_window_get_position: x = %d, y = %d\n", posx, posy);
00349         LOGPRINTF("rotation %d", gctx.rotation);
00350         LOGPRINTF("root_widht %d, root_height %d", root_width, root_height);
00351         LOGPRINTF("widht %d, height %d", width, height);
00352         LOGPRINTF("border %d", my_border);
00353 
00354         // Note: screen update called by on_expose event
00355         LOGPRINTF("exit");
00356     }
00357 
00358     void CNotepadWindow::disconnect_event_handlers(GtkWidget *widget)
00359     {
00360         source.disconnect_event_handlers(widget);
00361     }
00362 
00363     void CNotepadWindow::on_button_press(GdkEvent* event)
00364     {
00365         // Record the position inside the widget.
00366         GdkEventButton * button_event = reinterpret_cast<GdkEventButton*>(event);
00367         int win_x = static_cast<int>(button_event->x);
00368         int win_y = static_cast<int>(button_event->y);
00369 
00370         // Usually, on the device, only the left button.
00371         switch (button_event->button)
00372         {
00373             case 1:
00374                 {
00375                     CNPoint cpos(win_x, win_y);
00376                     LOGPRINTF("cpos %d, %d", win_x , win_y);
00377                     LOGPRINTF(">>cpos %d, %d", cpos.storageView().x, cpos.storageView().y);
00378 
00379                     if (erscribble_mode)
00380                     {
00381                         if (erscribble_status < 0)
00382                         {
00383                             erscribble_status = 0;
00384                             scribble(cpos, erscribble_status);
00385                         }
00386                     }
00387                     else
00388                     {
00389                         if (erase_status < 0)
00390                         {
00391                             erase_status = 0;
00392                             erase(cpos, erase_status);
00393                         }
00394                     }
00395                 }
00396                 break;
00397             default:
00398                 break;
00399         }
00400     }
00401 
00402     void CNotepadWindow::on_button_release(GdkEvent* event)
00403     {
00404         // Record the position inside the widget.
00405         GdkEventButton * button_event = reinterpret_cast<GdkEventButton *>(event);
00406         int win_x = static_cast<int>(button_event->x);
00407         int win_y = static_cast<int>(button_event->y);
00408 
00409         // Usually, on the device, only the left button.
00410         switch (button_event->button)
00411         {
00412             case 1:
00413                 {
00414                     CNPoint cpos(win_x, win_y);
00415 
00416                     if (erscribble_mode)
00417                     {
00418                         if (erscribble_status >= 0)
00419                         {
00420                             erscribble_status = 2;
00421                             scribble(cpos, erscribble_status);
00422 
00423                             // reset the status
00424                             erscribble_status = -1;
00425                         }
00426                     }
00427                     else
00428                     {
00429                         if (erase_status >= 0)
00430                         {
00431                             erase_status = 2;
00432                             erase(cpos, erase_status);
00433 
00434                             // reset the status
00435                             erase_status = -1;
00436                         }
00437                     }
00438                 }
00439                 break;
00440             default:
00441                 break;
00442         }
00443     }
00444 
00445     void CNotepadWindow::on_motion_notify(GdkEvent* event)
00446     {
00447         GdkEventButton * button_event = reinterpret_cast<GdkEventButton *>(event);
00448         int win_x = static_cast<int>(button_event->x);
00449         int win_y = static_cast<int>(button_event->y);
00450 
00451         CNPoint cpos(win_x, win_y);
00452 
00453         if (erscribble_mode)
00454         {
00455             if (erscribble_status == 0)
00456             {
00457                 erscribble_status = 1;
00458             }
00459 
00460             if (erscribble_status == 1)
00461             {
00462                 scribble(cpos, erscribble_status);
00463             }
00464         }
00465         else
00466         {
00467             if (erase_status == 0)
00468             {
00469                 erase_status = 1;
00470             }
00471 
00472             if (erase_status == 1)
00473             {
00474                 erase(cpos, erase_status);
00475             }
00476         }
00477     }
00478 
00479     void CNotepadWindow::on_key_press(GdkEvent* event)
00480     {
00481     }
00482 
00483     void CNotepadWindow::on_key_release(GdkEvent* event)
00484     {
00485         guint key_code = ((GdkEventKey*)event)->keyval;
00486 
00487         switch(key_code)
00488         {
00489             case GDK_Page_Up:
00490             case GDK_Home:
00491                 if (invert_pageturn)
00492                     goto_relative_page(5);  
00493                 else 
00494                     goto_relative_page(-5);  
00495                 break;
00496             case GDK_Page_Down:
00497             case GDK_End:
00498                 if (invert_pageturn)
00499                     goto_relative_page(-5);  
00500                 else 
00501                     goto_relative_page(5);  
00502                 break;
00503             case GDK_Up:
00504                 if (invert_pageturn)
00505                     pageflip_forward();
00506                 else 
00507                     pageflip_back();
00508                 break;
00509             case GDK_Down:
00510                 if (invert_pageturn)
00511                     pageflip_back();
00512                 else 
00513                     pageflip_forward();
00514                 break;
00515             case GDK_Left:
00516                 pageflip_back();
00517                 break;
00518             case GDK_Right:
00519                 pageflip_forward();
00520                 break;
00521             case GDK_Return:
00522             default:
00523                 break;
00524         }
00525     }
00526 
00527     void CNotepadWindow::on_delete(GdkEvent* event)
00528     {
00529         quit();
00530     }
00531 
00532     void CNotepadWindow::scribble(CNPoint& pos, int status)
00533     {
00534         switch(status)
00535         {
00536             case 0:
00537                 notepad_doc.on_scribble_begin(current_page, gctx, current_image, pos ); 
00538                 break;
00539             case 1:
00540                 notepad_doc.on_scribble_move(current_page, gctx, current_image, pos ); 
00541                 break;
00542             case 2:
00543                 notepad_doc.on_scribble_end(current_page, gctx, current_image, pos ); 
00544                 break;
00545             default:
00546                 break;
00547         }
00548     }
00549 
00550     void CNotepadWindow::erase(CNPoint& pos, int status)
00551     {
00552         switch(status)
00553         {
00554             case 0:
00555                 notepad_doc.on_erase_begin(current_page, gctx, current_image, pos);
00556                 break;
00557             case 1:
00558                 notepad_doc.on_erase_move(current_page, gctx, current_image, pos);
00559                 break;
00560             case 2:
00561                 notepad_doc.on_erase_end(current_page, gctx, current_image, pos);
00562                 notepad_pages.mark_dirty(current_page); // mark dirty, deleted lines may overlap existing lines, so redraw is required
00563                 break;
00564             default:
00565                 break;
00566         }
00567     }
00568 
00569     void CNotepadWindow::draw_page_margin()
00570     {
00571         // Draw two rectangles around the page as the margin: one lightgrey and one darkgrey.
00572         // The bottom margin is the statusbar in both potrait and landscape
00573         LOGPRINTF("entry");
00574 
00575         g_assert(main_window != 0);
00576         g_assert(main_window->window != 0);
00577 
00578         // Create a fresh graphics context, otherwise the line width will change with pen-size
00579         GdkGC* border_gc = gdk_gc_new(main_window->window);
00580         if (border_gc != 0)
00581         {
00582             gdk_gc_set_line_attributes(border_gc, 3, GDK_LINE_SOLID, GDK_CAP_PROJECTING, GDK_JOIN_MITER);
00583 
00584             GdkColor darkgrey = {0xAAAAAAAA, 0xAAAA, 0xAAAA, 0xAAAA}; 
00585 
00586             // outer rectangle
00587             gdk_gc_set_rgb_fg_color(border_gc, &darkgrey);
00588 
00589 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00590             gdk_draw_rectangle(main_window->window, border_gc, FALSE, gctx.margin-1, gctx.margin-1, (gctx.width - gctx.margin + 1), (gctx.height - gctx.margin + 1));
00591 #endif
00592 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
00593             gdk_draw_rectangle(main_window->window, border_gc, FALSE, gctx.margin-1, gctx.margin-1, (gctx.width - 2*gctx.margin + 2), (gctx.height - gctx.margin + 1));
00594 #endif
00595 
00596             g_object_unref(border_gc);
00597         }
00598         LOGPRINTF("exit");
00599     }
00600 
00601     void CNotepadWindow::draw_image_to_window()
00602     {
00603         // draw region of image within border to display
00604         if (current_image && main_window &&  main_window->window)
00605         {
00606 #if MACHINE_IS_DR1000S || MACHINE_IS_DR1000SW
00607             gdk_draw_image(GDK_DRAWABLE(main_window->window), gctx.gc, current_image, 
00608                 gctx.margin, gctx.margin, 
00609                 gctx.margin, gctx.margin, 
00610                 (gctx.width - gctx.margin), (gctx.height - gctx.margin));
00611 #endif
00612 #if MACHINE_IS_DR800S || MACHINE_IS_DR800SG || MACHINE_IS_DR800SW
00613             gdk_draw_image(GDK_DRAWABLE(main_window->window), gctx.gc, current_image, 
00614                 gctx.margin, gctx.margin, 
00615                 gctx.margin, gctx.margin, 
00616                 (gctx.width - 2*gctx.margin), (gctx.height - gctx.margin));
00617 #endif
00618         }
00619     }
00620 
00621     void CNotepadWindow::update(bool full)
00622     {
00623         LOGPRINTF("entry %d", current_page);
00624 
00625         g_assert(gctx.gc != 0);
00626 
00627         // Ask pages cache for page image. 
00628         current_image = notepad_pages.get_page(current_page);
00629 
00630         // draw current page image to screen
00631         draw_image_to_window();
00632 
00633         // draw gray page margin
00634         draw_page_margin(); 
00635 
00636         // screen update
00637         if (full) // default
00638         {
00639             notepad_stop_display_update(); // full display update 
00640             notepad_on_idle_display_yield(DM_HINT_FULL);
00641         }
00642         else
00643         {
00644             notepad_start_display_update(); // nonflashing display update
00645         }
00646 
00647         LOGPRINTF("exit");
00648     }
00649 
00650     void CNotepadWindow::goto_relative_page(gint delta)
00651     {
00652         gint pageno = current_page + delta;
00653         LOGPRINTF("current_page %d delta %d pageno %d", current_page, delta, pageno);
00654         goto_page(pageno);
00655     }
00656 
00657     void CNotepadWindow::on_jump_to_page_dialog_response(GtkDialog* dialog, gint pageno, gpointer data) 
00658     {
00659         LOGPRINTF("entry %d", pageno);
00660         g_assert(data != 0);
00661 
00662         if (pageno >= 0) // response is negative on cancel (GTK_RESPONSE codes) 
00663         {
00664             CNotepadWindow* instance = static_cast<CNotepadWindow*>(data);
00665             instance->goto_page(pageno);
00666         }
00667 
00668         menu_block(FALSE);
00669         gtk_widget_destroy(GTK_WIDGET(dialog));
00670 
00671         LOGPRINTF("exit");
00672     }
00673 
00674     void CNotepadWindow::goto_page() // show gotopage dialog
00675     {
00676         LOGPRINTF("entry");
00677         (void) save();
00678 
00679         menu_block(TRUE);
00680         GtkWidget* jumptopagedialog = ergtk_jump_to_page_dialog_new();
00681         g_signal_connect(jumptopagedialog,
00682                 "response", 
00683                 G_CALLBACK (CNotepadWindow::on_jump_to_page_dialog_response),
00684                 static_cast<gpointer>(this) );
00685         gtk_widget_show_all(jumptopagedialog);
00686         LOGPRINTF("exit");
00687     }
00688 
00689     void CNotepadWindow::goto_page(gint pageno)
00690     {
00691         LOGPRINTF("entry %d", pageno);
00692         (void) save();
00693 
00694         current_page = pageno;
00695         if (current_page > notepad_doc.get_num_pages()) // limit to range
00696         {
00697             current_page = notepad_doc.get_num_pages();
00698         }
00699         if (current_page < 1)
00700         {
00701             current_page = 1;
00702         }
00703         update_page_counter();
00704         update(); // show new page
00705 
00706         LOGPRINTF("exit");
00707     }
00708 
00709 
00710 
00711     void CNotepadWindow::pageflip_or_create()
00712     {
00713         LOGPRINTF("entry %d", pageflipto);
00714 
00715         // save previous page and set new page to screen 
00716         save();
00717 
00718         current_page = pageflipto;
00719 
00720         if (current_page < 1)
00721         {
00722             current_page = 1;
00723         }
00724 
00725         if (current_page > notepad_doc.get_num_pages()) 
00726         {
00727             current_page = notepad_doc.get_num_pages() + 1;
00728             notepad_doc.add_page(current_page);
00729             notepad_pages.insert_page(current_page);
00730             LOGPRINTF("increased: %d", current_page);
00731         }
00732 
00733         LOGPRINTF("to %d", current_page);
00734         update_page_counter();
00735         update(); // show new page
00736 
00737         LOGPRINTF("exit");
00738         doublepagefliptimout = 0;
00739     }
00740 
00741     gboolean static_pageflip(gpointer data)
00742     {
00743         CNotepadWindow* instance = (CNotepadWindow*) data;
00744         instance->pageflip_or_create(); // given pageno as class variable pageflipto
00745         return FALSE;
00746     }
00747 
00748 
00749     void CNotepadWindow::pageflip_back()
00750     {
00751         LOGPRINTF("entry");
00752 
00753         if (doublepagefliptimout) 
00754         {
00755             GSource* gsrc = g_main_context_find_source_by_id(NULL, doublepagefliptimout);
00756             g_source_destroy(gsrc); // restart delay
00757             --pageflipto;
00758         }
00759         else
00760         {
00761             pageflipto = current_page - 1;
00762         }
00763 
00764         // delay
00765         doublepagefliptimout = g_timeout_add(500, static_pageflip, this);
00766 
00767         LOGPRINTF("exit");
00768     }
00769 
00770     void CNotepadWindow::pageflip_forward()
00771     {
00772         LOGPRINTF("entry");
00773 
00774         if (doublepagefliptimout) 
00775         {
00776             GSource* gsrc = g_main_context_find_source_by_id(NULL, doublepagefliptimout);
00777             g_source_destroy(gsrc); // restart delay
00778             ++pageflipto;
00779         }
00780         else
00781         {
00782             pageflipto = current_page + 1;
00783         }
00784         // delay
00785         doublepagefliptimout = g_timeout_add(500, static_pageflip, this);
00786 
00787         LOGPRINTF("exit");
00788     }
00789 
00790     void CNotepadWindow::insert_page()
00791     {
00792         LOGPRINTF("entry");
00793         (void) save();
00794 
00795         notepad_doc.insert_page(current_page);
00796         // note: call pages.insert to keep pagecache in sync // note: perhaps place pages as layer between buffer and doc to prevent double call.
00797         notepad_pages.insert_page(current_page);
00798 
00799         update_page_counter();
00800         update(); // show new page
00801        
00802         LOGPRINTF("exit");
00803     }
00804 
00805 
00806     void CNotepadWindow::on_sureclear_dialog_response(GtkDialog* dialog, gint response, gpointer data) 
00807     {
00808         LOGPRINTF("entry");
00809         g_assert(data != 0);
00810         CNotepadWindow* instance = static_cast<CNotepadWindow*>(data);
00811 
00812         if (response == GTK_RESPONSE_YES)
00813         {
00814             instance->notepad_doc.clear_page(instance->current_page);
00815             instance->notepad_pages.clear_page(instance->current_page); // force page cache update
00816             instance->update(); // show new page
00817             (void) instance->save();
00818         }
00819 
00820         menu_block(FALSE);
00821         gtk_widget_destroy(GTK_WIDGET(dialog));
00822 
00823         LOGPRINTF("exit");
00824     }
00825 
00826     void CNotepadWindow::on_suredelete_dialog_response(GtkDialog* dialog, gint response, gpointer data) 
00827     {
00828         LOGPRINTF("entry");
00829         g_assert(data != 0);
00830         CNotepadWindow* instance = static_cast<CNotepadWindow*>(data);
00831 
00832         if (response == GTK_RESPONSE_YES)
00833         {
00834             instance->delete_current_page_impl();
00835         }
00836 
00837         menu_block(FALSE);
00838         gtk_widget_destroy(GTK_WIDGET(dialog));
00839 
00840         LOGPRINTF("exit");
00841     }
00842 
00843 
00844     void CNotepadWindow::clear_current_page()
00845     {
00846         LOGPRINTF("entry");
00847         GtkWidget* surecleardialog = gtk_message_dialog_new(
00848                                       GTK_WINDOW(main_window), 
00849                                       GTK_DIALOG_MODAL,
00850                                       GTK_MESSAGE_QUESTION, 
00851                                       GTK_BUTTONS_YES_NO,
00852                                       _("Are you sure you want to clear the writing from this page?") );
00853 
00854         g_signal_connect(surecleardialog,
00855                 "response", 
00856                 G_CALLBACK (CNotepadWindow::on_sureclear_dialog_response),
00857                 static_cast<gpointer>(this) );
00858         gtk_widget_show_all(surecleardialog);
00859 
00860         LOGPRINTF("exit");
00861     }    
00862 
00863     void CNotepadWindow::delete_current_page()
00864     {
00865         LOGPRINTF("entry");
00866         GtkWidget* suredeletedialog = gtk_message_dialog_new(
00867                                       GTK_WINDOW(main_window), 
00868                                       GTK_DIALOG_MODAL,
00869                                       GTK_MESSAGE_QUESTION, 
00870                                       GTK_BUTTONS_YES_NO,
00871                                       _("Are you sure you want to delete this page from the notepad?") );
00872 
00873         g_signal_connect(suredeletedialog,
00874                 "response", 
00875                 G_CALLBACK (CNotepadWindow::on_suredelete_dialog_response),
00876                 static_cast<gpointer>(this) );
00877         gtk_widget_show_all(suredeletedialog);
00878 
00879         LOGPRINTF("exit");
00880      }
00881 
00882     void CNotepadWindow::delete_current_page_impl()
00883     {
00884         LOGPRINTF("entry");
00885 
00886         if (notepad_doc.get_num_pages() <= 1)
00887         {
00888             LOGPRINTF("current_pages %d", current_page);
00889             current_page = 1;
00890             notepad_doc.clear_page(1);
00891             notepad_pages.clear_page(1); // force page cache update
00892         }
00893         else 
00894         {
00895             notepad_doc.delete_page(current_page);
00896             // note: call pages.delete to keep pagecache in sync
00897             // note: perhaps place pages as layer between buffer and doc to prevent double call.
00898             notepad_pages.delete_page(current_page);
00899             LOGPRINTF("current_pages %d", current_page);
00900 
00901             if (current_page > notepad_doc.get_num_pages())
00902             {
00903                 LOGPRINTF("current_pages %d", current_page);
00904                 current_page = notepad_doc.get_num_pages();
00905             }
00906         }
00907         update_page_counter();
00908         update(); // show new page
00909         
00910         LOGPRINTF("exit");
00911     }
00912     
00913     void CNotepadWindow::update_page_counter()
00914     {
00915         LOGPRINTF("entry");
00916         ipc_menu_set_pagecounter(current_page, notepad_doc.get_num_pages() );
00917     }
00918 
00919     void CNotepadWindow::on_failure_dialog_response(GtkDialog* dialog, gint response, gpointer data) 
00920     {
00921         LOGPRINTF("entry");
00922         g_assert(data != 0);
00923         CNotepadWindow* instance = static_cast<CNotepadWindow*>(data);
00924         instance->quit();
00925     }
00926 
00927     gboolean CNotepadWindow::save(bool update_last_read) 
00928     {
00929         if (gctx.width == 0 || gctx.height == 0)
00930         {
00931             WARNPRINTF("try to save in uninitialized state");
00932             return FALSE;
00933         }
00934 
00935         LOGPRINTF("entry");
00936 
00937         // save thumbnail of coverpage at changed page1
00938         if (current_page == 1 && notepad_doc.get_page_dirty())
00939         {
00940             // Note: Call get_page_dirty (to determine need to save thumbnail) before save_page, 
00941             //       because doc.save_page resets the dirtyflag
00942             CThumbnail saveThumbnail(*notepad_doc.getFileStore(), notepad_pages, gctx.margin);
00943         }
00944         // save page
00945         np_result res = notepad_doc.save_page(current_page, update_last_read);
00946 
00947         if (res != NP_OK) //exception
00948         {
00949             GtkWidget* save_failuredialog = gtk_message_dialog_new(
00950                                         GTK_WINDOW(main_window), 
00951                                         GTK_DIALOG_MODAL,
00952                                         GTK_MESSAGE_WARNING, 
00953                                         GTK_BUTTONS_OK,
00954                                         /* TRANSLATORS: 'Notes' refers to the name of the application */
00955                                         _("The changes to the page could not be saved. \n"
00956                                           "Notes must close because of this problem."));
00957 
00958 
00959             g_signal_connect(save_failuredialog,
00960                     "response", 
00961                     G_CALLBACK (CNotepadWindow::on_failure_dialog_response),
00962                     static_cast<gpointer>(this) );
00963             gtk_widget_show_all(save_failuredialog);
00964         }
00965 
00966         return (res == NP_OK);
00967     }
00968 
00969     // check on empty or invalid filename
00970     //TODO perhaps store on a more generic library location
00971     static gboolean check_filename_valid(const char* filename)
00972     {
00973         gboolean name_valid = TRUE;
00974         glong len = strlen(filename);
00975 
00976         if (len == 0)
00977         {
00978             // empty filename
00979             WARNPRINTF("rename to empty filename");
00980             name_valid = FALSE;
00981         }
00982         else if (len > 200)
00983         {
00984             // long filename
00985             WARNPRINTF("filename to long");
00986             name_valid = FALSE;
00987         }
00988         else if (filename[0] == '.')
00989         {
00990             // no . allowed on first character
00991             WARNPRINTF("hiddenfile not allowed");
00992             name_valid = FALSE;
00993         } 
00994         else if (g_unichar_isspace(filename[0]))
00995         {
00996             // no space allowed on first character
00997             WARNPRINTF("no spaces allowed on first character");
00998             name_valid = FALSE;
00999         }
01000         else
01001         {
01002             // check on illegal filename characters
01003             // note: filename can be utf8, but the illegal chars are ascii
01004             int n_illegal = strlen(ILLEGAL_FILENAME_CHARS);
01005             for (int i = 0; ((name_valid) && (i < n_illegal)); i++)
01006             {
01007                 if ( NULL != strchr(filename, ILLEGAL_FILENAME_CHARS[i]) )
01008                 {
01009                     LOGPRINTF("illegal character %c found", ILLEGAL_FILENAME_CHARS[i]);
01010                     name_valid = FALSE;
01011                 }
01012             }
01013         }
01014         return name_valid;
01015     }
01016    
01017     void CNotepadWindow::on_rename_dialog_response(GtkDialog* dialog, gint response, gpointer data) 
01018     {
01019         LOGPRINTF("entry");
01020         g_assert(data != 0);
01021         CNotepadWindow* instance = static_cast<CNotepadWindow*>(data);
01022 
01023         if (response == GTK_RESPONSE_ACCEPT)
01024         {   
01025             np_result rename_result = NP_OK;
01026 
01027             const gchar* newfilename = rename_get_text(); 
01028 
01029             gchar* my_file = instance->notepad_doc.get_filename_without_dir_and_ext();
01030             if (0 == g_strcmp0(newfilename, my_file))
01031             {
01032                 LOGPRINTF("rename to same file");
01033                 // NOTE: assumed window has saved page before rename.
01034                 rename_result = NP_OK;
01035             }
01036             else if ( ! check_filename_valid(newfilename) )
01037             {
01038                 LOGPRINTF("invalid filename entered %s", newfilename);
01039                 rename_result = NP_NO_FILENAME;
01040             }
01041             else
01042             {
01043                 rename_result = instance->notepad_doc.rename(newfilename);
01044             }
01045             g_free(my_file); my_file = 0;
01046 
01047 
01048             switch (rename_result)
01049             {
01050             case NP_OK:
01051                 break;
01052             case NP_NO_FILENAME:
01053                 {
01054                     notepad_stop_display_update();
01055                     GtkWidget* infodialog = gtk_message_dialog_new(
01056                                 GTK_WINDOW(instance->main_window), 
01057                                 GTK_DIALOG_MODAL,
01058                                 GTK_MESSAGE_WARNING, 
01059                                 GTK_BUTTONS_OK,
01060                                 _("The file name is not valid. \n"
01061                                   "Remember that  / \\ |: * ? < or > cannot be used in the name and it cannot begin with a space or period.") );
01062 
01063                     notepad_on_timeout_display_yield(400, DM_HINT_FULL);
01064                     gtk_dialog_run(GTK_DIALOG(infodialog));
01065                     gtk_widget_destroy(infodialog);
01066                     rename_set_focus();
01067                     // return, keep keyboard window alive to enter another name
01068                     return;
01069                 }
01070                 break;
01071             case NP_FILE_EXISTS:
01072                 {
01073                     notepad_stop_display_update();
01074                     GtkWidget* infodialog = gtk_message_dialog_new(
01075                                 GTK_WINDOW(instance->main_window), 
01076                                 GTK_DIALOG_MODAL,
01077                                 GTK_MESSAGE_WARNING, 
01078                                 GTK_BUTTONS_OK,
01079                                 _("This name has already been used for another note. \n"
01080                                   "Please choose a different name.") );
01081 
01082                     notepad_on_timeout_display_yield(400, DM_HINT_FULL);
01083                     gtk_dialog_run(GTK_DIALOG(infodialog));
01084                     gtk_widget_destroy(infodialog);
01085                     // return, keep keyboard window alive to enter another name
01086                     rename_set_focus();
01087                     return;
01088                 }
01089                 break;
01090             default:
01091                 {
01092                     notepad_stop_display_update();
01093                     GtkWidget* infodialog = gtk_message_dialog_new(
01094                                 GTK_WINDOW(instance->main_window), 
01095                                 GTK_DIALOG_MODAL,
01096                                 GTK_MESSAGE_WARNING, 
01097                                 GTK_BUTTONS_OK,
01098                                 _("The note could not be renamed for an unexpected reason. \n"
01099                                   "Please try again or contact customer support if you continue to experience this problem.") );
01100 
01101                     notepad_on_timeout_display_yield(400, DM_HINT_FULL);
01102                     gtk_dialog_run(GTK_DIALOG(infodialog));
01103                     gtk_widget_destroy(infodialog);
01104                 }
01105                 break;
01106             }
01107         }
01108         menu_block(FALSE);
01109         gtk_widget_destroy(GTK_WIDGET(dialog));
01110 
01111         LOGPRINTF("exit");
01112     }
01113 
01114     gboolean CNotepadWindow::rename() 
01115     {
01116         LOGPRINTF("entry");
01117 
01118         menu_block(TRUE);
01119         (void) save();
01120         gchar* my_file = notepad_doc.get_filename_without_dir_and_ext();
01121         GtkWidget* renamedialog = rename_dialog_create(main_window, my_file);
01122         g_signal_connect(renamedialog,
01123                 "response", 
01124                 G_CALLBACK (CNotepadWindow::on_rename_dialog_response),
01125                 static_cast<gpointer>(this) );
01126 
01127         g_free(my_file);
01128         my_file = 0;
01129         LOGPRINTF("exit");
01130         return FALSE;
01131     }
01132 
01133     void CNotepadWindow::set_scribble_mode(gboolean scribble_mode) // write/erase
01134     {
01135         erscribble_mode = scribble_mode;
01136         update(false); // fills the whitespaces in strokes overlapped with erased strokes
01137     }
01138     
01139     gboolean CNotepadWindow::get_scribble_mode() // get write/erase mode
01140     {
01141         return erscribble_mode;
01142     }
01143 
01144     void     CNotepadWindow::set_pageturn_inverted(gboolean is_inverted)
01145     { 
01146         invert_pageturn = is_inverted;
01147     }
01148 }
Generated by  doxygen 1.6.2-20100208