2021-02-11 14:20:23 -08:00
# include "draw.h"
2021-02-12 09:07:37 -08:00
# include "node.h"
2021-02-26 13:46:23 -08:00
# include "syntax_extension.h"
2021-02-25 14:08:18 -08:00
# include "strikethrough.h"
2021-02-15 10:03:07 -08:00
# include "mainwindow.h"
2021-02-15 07:26:30 -08:00
# include <gdk/gdkthreads.h>
2021-02-15 15:04:41 -08:00
# include <gdk/gdkselection.h>
2021-02-23 08:40:50 -08:00
# include <gtkmm/textiter.h>
2021-03-02 15:04:48 -08:00
# include <gdkmm/window.h>
2021-02-15 07:26:30 -08:00
# include <iostream>
2021-02-25 13:31:16 -08:00
# include <stdexcept>
2021-03-06 08:05:20 -08:00
# include <regex>
2021-02-25 13:31:16 -08:00
2021-02-12 09:07:37 -08:00
# define PANGO_SCALE_XXX_LARGE ((double)1.98)
2021-02-11 14:20:23 -08:00
2021-02-12 09:07:37 -08:00
struct DispatchData
{
2021-02-11 14:37:05 -08:00
GtkTextBuffer * buffer ;
2021-03-05 15:58:11 -08:00
// For inserting text
2021-02-11 14:37:05 -08:00
std : : string text ;
2021-02-15 08:10:13 -08:00
std : : string url ;
2021-03-05 15:58:11 -08:00
// For removing text
int charsTruncated ;
2021-03-09 14:13:58 -08:00
// Optional URL formatting
std : : string urlFont ;
2021-02-11 14:37:05 -08:00
} ;
2021-02-15 10:03:07 -08:00
Draw : : Draw ( MainWindow & mainWindow )
: mainWindow ( mainWindow ) ,
buffer ( Glib : : unwrap ( this - > get_buffer ( ) ) ) ,
2021-02-24 13:56:56 -08:00
addViewSourceMenuItem ( true ) ,
2021-02-15 07:26:30 -08:00
fontSize ( 10 * PANGO_SCALE ) ,
2021-02-12 13:40:58 -08:00
fontFamily ( " Ubuntu Monospace " ) ,
2021-02-12 12:46:41 -08:00
headingLevel ( 0 ) ,
listLevel ( 0 ) ,
isBold ( false ) ,
isItalic ( false ) ,
2021-02-26 13:46:23 -08:00
isStrikethrough ( false ) ,
2021-02-26 14:13:24 -08:00
isHighlight ( false ) ,
2021-03-04 16:18:42 -08:00
isSuperscript ( false ) ,
isSubscript ( false ) ,
2021-03-04 15:06:24 -08:00
isQuote ( false ) ,
2021-02-12 12:46:41 -08:00
bulletListLevel ( 0 ) ,
orderedListLevel ( 0 ) ,
isOrderedList ( false ) ,
2021-02-15 08:10:13 -08:00
isLink ( false ) ,
2021-03-02 15:04:48 -08:00
hovingOverLink ( false ) ,
2021-03-07 16:25:08 -08:00
defaultFont ( fontFamily ) ,
isUserAction ( false )
2021-02-11 14:20:23 -08:00
{
2021-02-18 15:44:21 -08:00
this - > disableEdit ( ) ;
2021-02-12 09:07:37 -08:00
set_indent ( 15 ) ;
set_left_margin ( 10 ) ;
set_right_margin ( 10 ) ;
2021-03-04 16:49:44 -08:00
set_top_margin ( 12 ) ;
set_bottom_margin ( 0 ) ;
2021-02-12 09:07:37 -08:00
set_monospace ( true ) ;
set_app_paintable ( true ) ;
2021-03-03 14:47:56 -08:00
set_pixels_above_lines ( 1 ) ;
2021-03-06 12:58:39 -08:00
set_pixels_below_lines ( 2 ) ;
set_pixels_inside_wrap ( 2 ) ;
2021-03-03 14:47:56 -08:00
set_wrap_mode ( Gtk : : WrapMode : : WRAP_WORD_CHAR ) ;
2021-02-12 09:07:37 -08:00
2021-03-02 15:04:48 -08:00
// Set cursors
auto display = get_display ( ) ;
normalCursor = Gdk : : Cursor : : create ( display , " default " ) ;
linkCursor = Gdk : : Cursor : : create ( display , " grab " ) ;
textCursor = Gdk : : Cursor : : create ( display , " text " ) ;
2021-02-16 15:06:33 -08:00
// Connect Signals
2021-02-15 10:03:07 -08:00
signal_event_after ( ) . connect ( sigc : : mem_fun ( this , & Draw : : event_after ) ) ;
2021-03-02 15:04:48 -08:00
signal_motion_notify_event ( ) . connect ( sigc : : mem_fun ( this , & Draw : : motion_notify_event ) ) ;
2021-02-16 15:06:33 -08:00
signal_populate_popup ( ) . connect ( sigc : : mem_fun ( this , & Draw : : populate_popup ) ) ;
2021-02-15 10:03:07 -08:00
}
/**
* Links can be activated by clicking or touching the screen .
*/
void Draw : : event_after ( GdkEvent * ev )
{
gdouble ex , ey ;
Gtk : : TextBuffer : : iterator iter ;
int x , y ;
if ( ev - > type = = GDK_BUTTON_RELEASE )
{
GdkEventButton * event ;
event = ( GdkEventButton * ) ev ;
if ( event - > button ! = GDK_BUTTON_PRIMARY )
return ;
ex = event - > x ;
ey = event - > y ;
}
else if ( ev - > type = = GDK_TOUCH_END )
{
GdkEventTouch * event ;
event = ( GdkEventTouch * ) ev ;
ex = event - > x ;
ey = event - > y ;
}
else
return ;
// Get the textview coordinates and retrieve an iterator
window_to_buffer_coords ( Gtk : : TextWindowType : : TEXT_WINDOW_WIDGET , ex , ey , x , y ) ;
get_iter_at_location ( iter , x , y ) ;
// Find the links
followLink ( iter ) ;
}
2021-03-02 15:04:48 -08:00
/**
* Update the cursor whenever there is a link
*/
bool Draw : : motion_notify_event ( GdkEventMotion * motion_event )
{
int x , y ;
window_to_buffer_coords ( Gtk : : TextWindowType : : TEXT_WINDOW_WIDGET , motion_event - > x , motion_event - > y , x , y ) ;
this - > changeCursor ( x , y ) ;
return false ;
}
2021-02-16 15:06:33 -08:00
/**
* Adapt right - click menu in textview
*/
void Draw : : populate_popup ( Gtk : : Menu * menu )
{
auto items = menu - > get_children ( ) ;
for ( auto * item : items )
{
Gtk : : MenuItem * menuItem = static_cast < Gtk : : MenuItem * > ( item ) ;
std : : string name = menuItem - > get_label ( ) ;
if ( name . compare ( " Cu_t " ) = = 0 )
{
menuItem - > set_label ( " Cu_t (Ctrl+X) " ) ;
}
else if ( name . compare ( " _Copy " ) = = 0 )
{
menuItem - > set_label ( " _Copy (Ctrl+C) " ) ;
}
else if ( name . compare ( " _Paste " ) = = 0 )
{
menuItem - > set_label ( " _Paste (Ctrl+V) " ) ;
}
else if ( name . compare ( " _Delete " ) = = 0 )
{
menuItem - > set_label ( " _Delete (Del) " ) ;
}
else if ( name . compare ( " Select _All " ) = = 0 )
{
menuItem - > set_label ( " Select _All (Ctrl+A) " ) ;
}
else if ( name . compare ( " Insert _Emoji " ) = = 0 )
{
item - > hide ( ) ;
}
}
2021-02-24 13:56:56 -08:00
if ( this - > addViewSourceMenuItem )
{
Gtk : : MenuItem * sourceCodeMenuItem = Gtk : : manage ( new Gtk : : MenuItem ( " View Source " , true ) ) ;
sourceCodeMenuItem - > signal_activate ( ) . connect ( source_code ) ;
sourceCodeMenuItem - > show ( ) ;
menu - > append ( * sourceCodeMenuItem ) ;
}
2021-02-16 15:06:33 -08:00
}
2021-02-11 14:20:23 -08:00
void Draw : : showMessage ( const std : : string & message , const std : : string & detailed_info )
{
2021-02-18 15:44:21 -08:00
if ( get_editable ( ) )
this - > disableEdit ( ) ;
this - > clearOnThread ( ) ;
2021-02-12 09:07:37 -08:00
2021-03-04 16:49:44 -08:00
this - > headingLevel = 1 ;
this - > insertText ( message ) ;
this - > headingLevel = 0 ;
2021-03-11 13:57:20 -08:00
this - > insertMarkupTextOnThread ( " \n \n " ) ;
2021-03-04 16:49:44 -08:00
this - > insertText ( detailed_info ) ;
2021-02-12 09:07:37 -08:00
}
void Draw : : showStartPage ( )
{
2021-02-18 15:44:21 -08:00
if ( get_editable ( ) )
this - > disableEdit ( ) ;
this - > clearOnThread ( ) ;
2021-02-12 09:07:37 -08:00
2021-03-04 16:49:44 -08:00
this - > headingLevel = 1 ;
2021-03-27 10:53:19 -07:00
this - > insertText ( " 🚀🌍 Welcome to the Decentralized Web (DWeb) " ) ;
2021-03-04 16:49:44 -08:00
this - > headingLevel = 0 ;
2021-03-09 14:41:01 -08:00
this - > insertMarkupTextOnThread ( " \n \n " ) ;
2021-03-04 16:49:44 -08:00
this - > insertText ( " You can surf the web as intended via LibreWeb, by using IPFS as a decentralized solution. This is also the fastest browser in the world. \n \n \
2021-03-01 14:27:26 -08:00
The content is fully written in markdown format , allowing you to easily publish your own site , blog article or e - book . \ n \
This browser has even a built - in editor . Check it out in the menu : File - > New Document ! \ n \ n " );
2021-03-04 16:49:44 -08:00
this - > insertText ( " See an example page hosted on IPFS: " ) ;
2021-03-27 19:03:54 -07:00
this - > insertLink ( " Click here for the example page " , " ipfs://QmcuUv9QXR86vXvSAQPkRJfi5kJwtTtu1NftNaWWVNftSd " , defaultFont . to_string ( ) ) ;
2021-02-12 09:07:37 -08:00
}
/**
2021-02-24 13:56:56 -08:00
* Process AST document ( markdown format ) and draw the text in the GTK TextView
2021-02-12 09:07:37 -08:00
*/
void Draw : : processDocument ( cmark_node * root_node )
{
2021-02-18 15:44:21 -08:00
if ( get_editable ( ) )
this - > disableEdit ( ) ;
this - > clearOnThread ( ) ;
2021-02-12 09:07:37 -08:00
// Loop over AST nodes
cmark_event_type ev_type ;
cmark_iter * iter = cmark_iter_new ( root_node ) ;
while ( ( ev_type = cmark_iter_next ( iter ) ) ! = CMARK_EVENT_DONE )
{
cmark_node * cur = cmark_iter_get_node ( iter ) ;
2021-02-25 13:31:16 -08:00
try
{
processNode ( cur , ev_type ) ;
}
catch ( const std : : runtime_error & error )
{
std : : cerr < < " Error: Processing node failed, with message: " < < error . what ( ) < < std : : endl ;
// Continue nevertheless
}
2021-02-12 09:07:37 -08:00
}
}
2021-02-24 14:39:32 -08:00
void Draw : : setViewSourceMenuItem ( bool isEnabled )
{
this - > addViewSourceMenuItem = isEnabled ;
}
void Draw : : newDocument ( )
{
2021-03-07 16:25:08 -08:00
this - > undoPool . clear ( ) ;
this - > redoPool . clear ( ) ;
2021-02-24 14:39:32 -08:00
this - > clearText ( ) ;
2021-03-07 16:25:08 -08:00
2021-02-24 14:39:32 -08:00
enableEdit ( ) ;
grab_focus ( ) ; // Claim focus on text view
}
std : : string Draw : : getText ( )
{
return get_buffer ( ) . get ( ) - > get_text ( ) ;
}
void Draw : : clearText ( )
{
auto buffer = get_buffer ( ) ;
buffer - > erase ( buffer - > begin ( ) , buffer - > end ( ) ) ;
}
void Draw : : enableEdit ( )
{
set_editable ( true ) ;
set_cursor_visible ( true ) ;
2021-03-07 16:25:08 -08:00
auto buffer = get_buffer ( ) ;
this - > beginUserActionSignalHandler = buffer - > signal_begin_user_action ( ) . connect ( sigc : : mem_fun ( this , & Draw : : begin_user_action ) , false ) ;
this - > endUserActionSignalHandler = buffer - > signal_end_user_action ( ) . connect ( sigc : : mem_fun ( this , & Draw : : end_user_action ) , false ) ;
this - > insertTextSignalHandler = buffer - > signal_insert ( ) . connect ( sigc : : mem_fun ( this , & Draw : : on_insert ) , false ) ;
this - > deleteTextSignalHandler = buffer - > signal_erase ( ) . connect ( sigc : : mem_fun ( this , & Draw : : on_delete ) , false ) ;
2021-02-24 14:39:32 -08:00
}
void Draw : : disableEdit ( )
{
set_editable ( false ) ;
set_cursor_visible ( false ) ;
2021-03-07 16:25:08 -08:00
// Disconnect signal handles
this - > beginUserActionSignalHandler . disconnect ( ) ;
this - > endUserActionSignalHandler . disconnect ( ) ;
this - > insertTextSignalHandler . disconnect ( ) ;
this - > deleteTextSignalHandler . disconnect ( ) ;
2021-02-24 14:39:32 -08:00
}
/**
* Search for links
*/
void Draw : : followLink ( Gtk : : TextBuffer : : iterator & iter )
{
auto tags = iter . get_tags ( ) ;
for ( auto const & tag : tags )
{
char * url = static_cast < char * > ( tag - > get_data ( " url " ) ) ;
if ( url ! = 0 & & ( strlen ( url ) > 0 ) )
{
// Get the URL
mainWindow . doRequest ( url , true ) ;
break ;
}
}
}
2021-03-07 16:25:08 -08:00
/**
* Undo action ( Ctrl + Z )
*/
void Draw : : undo ( )
{
if ( get_editable ( ) & & ( undoPool . size ( ) > 0 ) )
{
auto undoAction = undoPool . at ( undoPool . size ( ) - 1 ) ;
auto buffer = get_buffer ( ) ;
undoPool . pop_back ( ) ;
if ( undoAction . isInsert )
{
Gtk : : TextBuffer : : iterator startIter = buffer - > get_iter_at_offset ( undoAction . beginOffset ) ;
Gtk : : TextBuffer : : iterator endIter = buffer - > get_iter_at_offset ( undoAction . endOffset ) ;
buffer - > erase ( startIter , endIter ) ;
buffer - > place_cursor ( buffer - > get_iter_at_offset ( undoAction . beginOffset ) ) ;
}
else
{
Gtk : : TextBuffer : : iterator startIter = buffer - > get_iter_at_offset ( undoAction . beginOffset ) ;
buffer - > insert ( startIter , undoAction . text ) ;
buffer - > place_cursor ( buffer - > get_iter_at_offset ( undoAction . endOffset ) ) ;
}
redoPool . push_back ( undoAction ) ;
}
}
/**
* Redo action ( Ctrl + Y )
*/
void Draw : : redo ( )
2021-02-15 15:04:41 -08:00
{
2021-03-07 16:25:08 -08:00
if ( get_editable ( ) & & ( redoPool . size ( ) > 0 ) )
2021-02-17 12:44:02 -08:00
{
2021-03-07 16:25:08 -08:00
auto redoAction = redoPool . at ( redoPool . size ( ) - 1 ) ;
2021-02-17 12:44:02 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
redoPool . pop_back ( ) ;
if ( redoAction . isInsert )
{
Gtk : : TextBuffer : : iterator startIter = buffer - > get_iter_at_offset ( redoAction . beginOffset ) ;
buffer - > insert ( startIter , redoAction . text ) ;
buffer - > place_cursor ( buffer - > get_iter_at_offset ( redoAction . endOffset ) ) ;
}
else
{
Gtk : : TextBuffer : : iterator startIter = buffer - > get_iter_at_offset ( redoAction . beginOffset ) ;
Gtk : : TextBuffer : : iterator endIter = buffer - > get_iter_at_offset ( redoAction . endOffset ) ;
buffer - > erase ( startIter , endIter ) ;
buffer - > place_cursor ( buffer - > get_iter_at_offset ( redoAction . beginOffset ) ) ;
}
undoPool . push_back ( redoAction ) ;
}
}
void Draw : : cut ( )
{
if ( get_editable ( ) )
{
2021-03-03 10:03:55 -08:00
auto clipboard = get_clipboard ( " CLIPBOARD " ) ;
2021-03-07 16:25:08 -08:00
get_buffer ( ) - > cut_clipboard ( clipboard ) ;
2021-02-17 12:44:02 -08:00
}
2021-03-03 10:03:55 -08:00
else
2021-02-15 15:04:41 -08:00
{
2021-03-03 10:03:55 -08:00
auto clipboard = get_clipboard ( " CLIPBOARD " ) ;
2021-03-07 16:25:08 -08:00
get_buffer ( ) - > copy_clipboard ( clipboard ) ;
2021-02-15 15:04:41 -08:00
}
2021-02-17 12:44:02 -08:00
}
void Draw : : copy ( )
{
2021-03-03 10:03:55 -08:00
auto clipboard = get_clipboard ( " CLIPBOARD " ) ;
2021-03-07 16:25:08 -08:00
get_buffer ( ) - > copy_clipboard ( clipboard ) ;
2021-02-15 15:04:41 -08:00
}
void Draw : : paste ( )
{
2021-03-07 16:25:08 -08:00
if ( get_editable ( ) )
2021-02-15 15:04:41 -08:00
{
auto clipboard = get_clipboard ( " CLIPBOARD " ) ;
2021-03-07 16:25:08 -08:00
get_buffer ( ) - > paste_clipboard ( clipboard ) ;
2021-02-15 15:04:41 -08:00
}
}
2021-02-17 12:11:07 -08:00
void Draw : : del ( )
{
2021-03-07 16:25:08 -08:00
if ( get_editable ( ) )
2021-02-17 12:11:07 -08:00
{
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-17 12:11:07 -08:00
Gtk : : TextBuffer : : iterator begin , end ;
if ( buffer - > get_selection_bounds ( begin , end ) )
{
buffer - > erase ( begin , end ) ;
}
2021-02-23 07:54:28 -08:00
else
{
2021-02-23 09:01:28 -08:00
+ + end ;
2021-02-23 07:54:28 -08:00
buffer - > erase ( begin , end ) ;
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-17 12:11:07 -08:00
}
}
2021-03-03 10:03:55 -08:00
void Draw : : selectAll ( )
{
auto buffer = get_buffer ( ) ;
buffer - > select_range ( buffer - > begin ( ) , buffer - > end ( ) ) ;
}
2021-02-19 10:38:05 -08:00
/*************************************************************
2021-02-24 14:39:32 -08:00
* Editor signals calls
2021-02-19 10:38:05 -08:00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-02-19 13:58:55 -08:00
void Draw : : make_heading ( int headingLevel )
{
2021-03-05 15:58:11 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 13:58:55 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-19 13:58:55 -08:00
std : : string heading = std : : string ( headingLevel , ' # ' ) ;
2021-03-05 15:58:11 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( heading + " " + text ) ;
}
else
{
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( heading + " \n " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + headingLevel + 1 ) ;
buffer - > place_cursor ( newCursorPos ) ;
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 13:58:55 -08:00
}
2021-02-19 10:38:05 -08:00
void Draw : : make_bold ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " ** " + text + " ** " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " **** " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 2 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_italic ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " * " + text + " * " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " ** " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 1 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_strikethrough ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " ~~ " + text + " ~~ " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " ~~~~ " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 2 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_super ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " ^ " + text + " ^ " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " ^^ " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 1 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_sub ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
2021-03-06 08:09:46 -08:00
buffer - > insert_at_cursor ( " % " + text + " % " ) ;
2021-02-23 07:54:28 -08:00
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
2021-03-06 08:09:46 -08:00
buffer - > insert_at_cursor ( " %% " ) ;
2021-02-23 08:40:50 -08:00
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 1 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_quote ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
std : : istringstream iss ( text ) ;
std : : string line ;
while ( std : : getline ( iss , line ) )
{
2021-03-12 13:55:55 -08:00
buffer - > insert_at_cursor ( " > " + line + " \n " ) ;
2021-02-23 07:54:28 -08:00
}
}
else
{
2021-03-05 15:58:11 -08:00
buffer - > insert_at_cursor ( " \n > text " ) ; // TODO: only insert new line if there is non before
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : insert_link ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
2021-02-23 07:54:28 -08:00
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
2021-02-23 08:40:50 -08:00
buffer - > insert_at_cursor ( " [ " + text + " ](ipfs://url) " ) ;
auto beginCursorPos = buffer - > get_iter_at_offset ( insertOffset + text . length ( ) + 10 ) ;
2021-02-24 13:56:56 -08:00
auto endCursorPos = buffer - > get_iter_at_offset ( insertOffset + text . length ( ) + 13 ) ;
2021-02-23 08:40:50 -08:00
buffer - > select_range ( beginCursorPos , endCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
2021-02-24 14:39:32 -08:00
buffer - > insert_at_cursor ( " [link](ipfs://url) " ) ;
auto beginCursorPos = buffer - > get_iter_at_offset ( insertOffset + 14 ) ;
auto endCursorPos = buffer - > get_iter_at_offset ( insertOffset + 17 ) ;
2021-02-23 08:40:50 -08:00
buffer - > select_range ( beginCursorPos , endCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : insert_image ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " ![]( " + text + " ] " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " ![](ipfs://image.jpg) " ) ;
auto beginCursorPos = buffer - > get_iter_at_offset ( insertOffset + 11 ) ;
auto endCursorPos = buffer - > get_iter_at_offset ( insertOffset + 20 ) ;
buffer - > select_range ( beginCursorPos , endCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
2021-03-05 15:58:11 -08:00
void Draw : : make_code ( )
{
Gtk : : TextBuffer : : iterator start , end ;
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-03-05 15:58:11 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
// Strip begin & end line breaks
if ( text . starts_with ( ' \n ' ) )
{
text . erase ( 0 , 1 ) ;
}
if ( text . ends_with ( ' \n ' ) )
{
text . erase ( text . size ( ) - 1 ) ;
}
if ( text . find ( ' \n ' ) ! = std : : string : : npos )
{
// Insert code block
buffer - > insert_at_cursor ( " ``` \n " + text + " \n ``` \n " ) ;
}
else
{
// Insert inline code
buffer - > insert_at_cursor ( " ` " + text + " ` " ) ;
}
}
else
{
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " `` " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 1 ) ;
buffer - > place_cursor ( newCursorPos ) ;
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-03-05 15:58:11 -08:00
}
2021-02-19 10:38:05 -08:00
void Draw : : insert_bullet_list ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
std : : istringstream iss ( text ) ;
std : : string line ;
while ( std : : getline ( iss , line ) )
{
buffer - > insert_at_cursor ( " * " + line + " \n " ) ;
}
}
else
{
2021-02-23 08:40:50 -08:00
buffer - > insert_at_cursor ( " \n * " ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : insert_numbered_list ( )
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-19 10:38:05 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
std : : istringstream iss ( text ) ;
std : : string line ;
int counter = 1 ;
while ( std : : getline ( iss , line ) )
{
buffer - > insert_at_cursor ( std : : to_string ( counter ) + " . " + line + " \n " ) ;
counter + + ;
}
}
else
{
2021-02-23 08:40:50 -08:00
buffer - > insert_at_cursor ( " \n 1. " ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
2021-02-19 10:38:05 -08:00
}
void Draw : : make_highlight ( )
2021-02-18 15:44:21 -08:00
{
2021-02-23 07:54:28 -08:00
Gtk : : TextBuffer : : iterator start , end ;
2021-02-18 15:44:21 -08:00
auto buffer = get_buffer ( ) ;
2021-03-07 16:25:08 -08:00
buffer - > begin_user_action ( ) ;
2021-02-23 07:54:28 -08:00
if ( buffer - > get_selection_bounds ( start , end ) )
{
std : : string text = buffer - > get_text ( start , end ) ;
buffer - > erase_selection ( ) ;
buffer - > insert_at_cursor ( " == " + text + " == " ) ;
}
else
{
2021-02-23 08:40:50 -08:00
int insertOffset = buffer - > get_insert ( ) - > get_iter ( ) . get_offset ( ) ;
buffer - > insert_at_cursor ( " ==== " ) ;
auto newCursorPos = buffer - > get_iter_at_offset ( insertOffset + 2 ) ;
buffer - > place_cursor ( newCursorPos ) ;
2021-02-23 07:54:28 -08:00
}
2021-03-07 16:25:08 -08:00
buffer - > end_user_action ( ) ;
}
void Draw : : begin_user_action ( )
{
this - > isUserAction = true ;
}
void Draw : : end_user_action ( )
{
this - > isUserAction = false ;
}
/**
* Triggered when text gets inserted
*/
void Draw : : on_insert ( const Gtk : : TextBuffer : : iterator & pos , const Glib : : ustring & text , int bytes __attribute__ ( ( unused ) ) )
{
if ( this - > isUserAction )
{
UndoRedoData undoData = { } ;
undoData . isInsert = true ;
undoData . beginOffset = pos . get_offset ( ) ;
undoData . endOffset = pos . get_offset ( ) + text . size ( ) ;
undoData . text = text ;
this - > undoPool . push_back ( undoData ) ;
this - > redoPool . clear ( ) ;
}
}
/**
* Triggered when text gets deleted / removed
*/
void Draw : : on_delete ( const Gtk : : TextBuffer : : iterator & range_start , const Gtk : : TextBuffer : : iterator & range_end )
{
if ( this - > isUserAction )
{
auto text = get_buffer ( ) - > get_text ( range_start , range_end ) ;
UndoRedoData undoData = { } ;
undoData . isInsert = false ;
undoData . beginOffset = range_start . get_offset ( ) ;
undoData . endOffset = range_end . get_offset ( ) ;
undoData . text = text ;
this - > undoPool . push_back ( undoData ) ;
}
2021-02-18 15:44:21 -08:00
}
2021-02-24 14:39:32 -08:00
/************************************************
* Private methods
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-02-12 09:07:37 -08:00
/**
* Process and parse each node in the AST
*/
void Draw : : processNode ( cmark_node * node , cmark_event_type ev_type )
{
bool entering = ( ev_type = = CMARK_EVENT_ENTER ) ;
2021-02-26 13:46:23 -08:00
// Take care of the markdown extensions
if ( node - > extension )
{
if ( strcmp ( node - > extension - > name , " strikethrough " ) = = 0 )
{
isStrikethrough = entering ;
2021-02-26 14:13:24 -08:00
return ;
}
else if ( strcmp ( node - > extension - > name , " highlight " ) = = 0 )
{
isHighlight = entering ;
return ;
2021-02-26 13:46:23 -08:00
}
2021-03-04 16:18:42 -08:00
else if ( strcmp ( node - > extension - > name , " superscript " ) = = 0 )
{
isSuperscript = entering ;
return ;
}
else if ( strcmp ( node - > extension - > name , " subscript " ) = = 0 )
{
isSubscript = entering ;
return ;
}
2021-02-26 13:46:23 -08:00
}
2021-02-12 09:07:37 -08:00
switch ( node - > type )
{
case CMARK_NODE_DOCUMENT :
if ( entering )
{
2021-03-04 15:06:24 -08:00
// Reset all (better safe than sorry)
2021-02-12 09:07:37 -08:00
headingLevel = 0 ;
bulletListLevel = 0 ;
2021-02-26 13:46:23 -08:00
orderedListLevel = 0 ;
2021-02-12 09:07:37 -08:00
listLevel = 0 ;
2021-02-26 13:46:23 -08:00
isOrderedList = false ;
isBold = false ;
isItalic = false ;
isStrikethrough = false ;
2021-02-26 14:13:24 -08:00
isHighlight = false ;
2021-03-04 16:18:42 -08:00
isSuperscript = false ;
isSubscript = false ;
2021-03-04 15:06:24 -08:00
isQuote = false ;
2021-02-12 09:07:37 -08:00
}
break ;
case CMARK_NODE_BLOCK_QUOTE :
2021-03-04 15:06:24 -08:00
isQuote = entering ;
2021-03-05 15:58:11 -08:00
if ( ! entering )
{
// Replace last quote '|'-sign with a normal blank line
this - > truncateText ( 2 ) ;
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " \n " ) ;
2021-03-05 15:58:11 -08:00
}
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_LIST :
{
cmark_list_type listType = node - > as . list . list_type ;
2021-03-05 09:13:06 -08:00
2021-02-12 09:07:37 -08:00
if ( entering )
{
listLevel + + ;
}
else
{
listLevel - - ;
}
if ( listLevel = = 0 )
{
// Reset bullet/ordered levels
bulletListLevel = 0 ;
orderedListLevel = 0 ;
isOrderedList = false ;
2021-03-05 15:58:11 -08:00
if ( ! entering )
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " \n " ) ;
2021-02-12 09:07:37 -08:00
}
else if ( listLevel > 0 )
{
if ( entering )
{
if ( listType = = cmark_list_type : : CMARK_BULLET_LIST )
{
bulletListLevel + + ;
}
else if ( listType = = cmark_list_type : : CMARK_ORDERED_LIST )
{
orderedListLevel + + ;
// Create the counter (and reset to zero)
orderedListCounters [ orderedListLevel ] = 0 ;
}
}
else
{
2021-02-27 13:22:30 -08:00
// Un-indent list level again
2021-02-12 09:07:37 -08:00
if ( listType = = cmark_list_type : : CMARK_BULLET_LIST )
{
bulletListLevel - - ;
}
else if ( listType = = cmark_list_type : : CMARK_ORDERED_LIST )
{
orderedListLevel - - ;
}
}
isOrderedList = ( orderedListLevel > 0 ) & & ( bulletListLevel < = 0 ) ;
}
}
break ;
case CMARK_NODE_ITEM :
2021-03-05 09:13:06 -08:00
if ( entering )
2021-02-12 09:07:37 -08:00
{
2021-03-05 09:13:06 -08:00
if ( isOrderedList )
{
// Increasement ordered list counter
orderedListCounters [ orderedListLevel ] + + ;
}
// Insert tabs & bullet/number
if ( bulletListLevel > 0 )
{
if ( bulletListLevel % 2 = = 0 )
{
this - > insertText ( std : : string ( bulletListLevel , ' \ u0009 ' ) + " \u25e6 " ) ;
}
else
{
this - > insertText ( std : : string ( bulletListLevel , ' \ u0009 ' ) + " \u2022 " ) ;
}
}
else if ( orderedListLevel > 0 )
{
std : : string number ;
if ( orderedListLevel % 2 = = 0 )
{
number = Draw : : intToRoman ( orderedListCounters [ orderedListLevel ] ) + " " ;
}
else
{
number = std : : to_string ( orderedListCounters [ orderedListLevel ] ) + " . " ;
}
this - > insertText ( std : : string ( orderedListLevel , ' \ u0009 ' ) + number ) ;
}
}
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_HEADING :
if ( entering )
{
headingLevel = node - > as . heading . level ;
}
else
{
2021-03-09 14:30:07 -08:00
// Insert line break after heading
this - > insertMarkupTextOnThread ( " \n \n " ) ;
2021-02-12 09:07:37 -08:00
headingLevel = 0 ; // reset
}
break ;
case CMARK_NODE_CODE_BLOCK :
2021-02-25 09:28:49 -08:00
{
std : : string code = cmark_node_get_literal ( node ) ;
2021-03-05 15:58:11 -08:00
std : : string newline = ( isQuote ) ? " " : " \n " ;
2021-03-09 14:13:58 -08:00
this - > insertText ( code + newline , " " , CodeTypeEnum : : CODE_BLOCK ) ;
2021-02-25 09:28:49 -08:00
}
2021-02-25 13:31:16 -08:00
break ;
2021-02-12 09:07:37 -08:00
case CMARK_NODE_HTML_BLOCK :
break ;
case CMARK_NODE_CUSTOM_BLOCK :
break ;
case CMARK_NODE_THEMATIC_BREAK :
{
2021-03-04 16:49:44 -08:00
this - > isBold = true ;
this - > insertText ( " \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \u2015 \n \n " ) ;
this - > isBold = false ;
2021-02-12 09:07:37 -08:00
}
break ;
case CMARK_NODE_PARAGRAPH :
2021-03-05 15:58:11 -08:00
// For listings only insert a single new line
if ( ! entering & & ( listLevel > 0 ) )
2021-02-12 14:30:39 -08:00
{
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " \n " ) ;
2021-03-05 15:58:11 -08:00
}
// Dealing with paragraphs in quotes
else if ( entering & & isQuote )
{
this - > insertText ( " \uFF5C " ) ;
}
else if ( ! entering & & isQuote )
{
this - > insertText ( " \n \uFF5C \n " ) ;
}
// Normal paragraph, just blank line
else if ( ! entering )
{
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " \n \n " ) ;
2021-02-12 13:40:58 -08:00
}
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_TEXT :
{
std : : string text = cmark_node_get_literal ( node ) ;
2021-02-15 08:10:13 -08:00
// URL
2021-03-04 16:49:44 -08:00
if ( isLink )
2021-02-15 08:10:13 -08:00
{
2021-03-09 14:13:58 -08:00
this - > insertText ( text , linkURL ) ;
2021-02-15 08:10:13 -08:00
linkURL = " " ;
2021-02-12 09:07:37 -08:00
}
2021-03-04 16:18:42 -08:00
// Text (with optional inline formatting)
2021-02-12 09:07:37 -08:00
else
{
2021-03-04 16:49:44 -08:00
this - > insertText ( text ) ;
2021-02-12 09:07:37 -08:00
}
}
break ;
case CMARK_NODE_LINEBREAK :
2021-02-12 13:40:58 -08:00
// Hard brake
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " \n " ) ;
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_SOFTBREAK :
2021-02-12 13:40:58 -08:00
// only insert space
2021-03-09 14:30:07 -08:00
this - > insertMarkupTextOnThread ( " " ) ;
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_CODE :
2021-02-25 09:28:49 -08:00
{
std : : string code = cmark_node_get_literal ( node ) ;
2021-03-09 14:13:58 -08:00
this - > insertText ( code , " " , CodeTypeEnum : : INLINE_CODE ) ;
2021-02-25 09:28:49 -08:00
}
break ;
2021-02-12 09:07:37 -08:00
case CMARK_NODE_HTML_INLINE :
break ;
case CMARK_NODE_CUSTOM_INLINE :
break ;
case CMARK_NODE_STRONG :
isBold = entering ;
break ;
case CMARK_NODE_EMPH :
isItalic = entering ;
break ;
case CMARK_NODE_LINK :
2021-02-15 08:10:13 -08:00
isLink = entering ;
if ( entering )
{
linkURL = cmark_node_get_url ( node ) ;
}
2021-02-12 09:07:37 -08:00
break ;
case CMARK_NODE_IMAGE :
break ;
case CMARK_NODE_FOOTNOTE_REFERENCE :
break ;
case CMARK_NODE_FOOTNOTE_DEFINITION :
break ;
default :
2021-03-04 16:18:42 -08:00
throw std : : runtime_error ( " Node type ' " + std : : string ( cmark_node_get_type_string ( node ) ) + " ' not found. " ) ;
2021-02-12 09:07:37 -08:00
break ;
}
}
2021-02-18 15:44:21 -08:00
/**
* Insert markup text - thread safe
*/
2021-03-11 13:57:20 -08:00
void Draw : : insertText ( std : : string text , const std : : string & url , CodeTypeEnum codeType )
2021-02-12 09:07:37 -08:00
{
2021-03-04 16:18:42 -08:00
auto font = defaultFont ;
std : : string span ;
2021-03-11 13:57:20 -08:00
span . reserve ( 80 ) ;
2021-03-04 16:18:42 -08:00
std : : string foreground ;
std : : string background ;
2021-03-11 13:57:20 -08:00
// Use by reference to replace the string
2021-03-12 13:44:48 -08:00
this - > encodeText ( text ) ;
2021-03-06 08:05:20 -08:00
2021-03-04 16:18:42 -08:00
if ( isStrikethrough )
{
2021-03-11 13:57:20 -08:00
span . append ( " strikethrough= \" true \" " ) ;
2021-03-04 16:18:42 -08:00
}
if ( isSuperscript )
{
font . set_size ( 8000 ) ;
2021-03-11 13:57:20 -08:00
span . append ( " rise= \" 6000 \" " ) ;
2021-03-04 16:18:42 -08:00
}
2021-03-12 13:59:12 -08:00
// You can not have superscript & subscript applied together
else if ( isSubscript )
2021-03-04 16:18:42 -08:00
{
2021-03-04 16:49:44 -08:00
font . set_size ( 8000 ) ;
2021-03-11 13:57:20 -08:00
span . append ( " rise= \" -6000 \" " ) ;
2021-03-04 16:18:42 -08:00
}
if ( isBold )
{
font . set_weight ( Pango : : WEIGHT_BOLD ) ;
}
if ( isItalic )
{
font . set_style ( Pango : : STYLE_ITALIC ) ;
}
if ( isHighlight )
{
foreground = " black " ;
background = " #FFFF00 " ;
}
2021-03-05 15:58:11 -08:00
if ( codeType ! = Draw : : CodeTypeEnum : : NONE )
{
foreground = " #323232 " ;
background = " #e0e0e0 " ;
}
2021-03-04 16:49:44 -08:00
if ( headingLevel > 0 )
{
font . set_weight ( Pango : : WEIGHT_BOLD ) ;
switch ( headingLevel )
{
case 1 :
font . set_size ( fontSize * PANGO_SCALE_XXX_LARGE ) ;
break ;
case 2 :
font . set_size ( fontSize * PANGO_SCALE_XX_LARGE ) ;
break ;
case 3 :
font . set_size ( fontSize * PANGO_SCALE_X_LARGE ) ;
break ;
case 4 :
font . set_size ( fontSize * PANGO_SCALE_LARGE ) ;
break ;
case 5 :
font . set_size ( fontSize * PANGO_SCALE_MEDIUM ) ;
break ;
case 6 :
font . set_size ( fontSize * PANGO_SCALE_MEDIUM ) ;
foreground = " gray " ;
break ;
default :
break ;
}
}
2021-03-05 15:58:11 -08:00
if ( isQuote )
{
foreground = " blue " ;
}
2021-03-04 16:18:42 -08:00
if ( ! foreground . empty ( ) )
{
2021-03-11 13:57:20 -08:00
span . append ( " foreground= \" " + foreground + " \" " ) ;
2021-03-04 16:18:42 -08:00
}
if ( ! background . empty ( ) )
{
2021-03-11 13:57:20 -08:00
span . append ( " background= \" " + background + " \" " ) ;
2021-03-04 16:18:42 -08:00
}
2021-03-11 13:57:20 -08:00
span . append ( " font_desc= \" " + font . to_string ( ) + " \" " ) ;
2021-03-05 15:58:11 -08:00
2021-03-09 14:13:58 -08:00
// Insert URL
if ( ! url . empty ( ) )
{
2021-03-11 13:57:20 -08:00
this - > insertLink ( text , url , font . to_string ( ) ) ;
2021-03-09 14:13:58 -08:00
}
2021-03-09 14:30:07 -08:00
// Insert text/heading
2021-03-04 16:49:44 -08:00
else
{
2021-03-05 15:58:11 -08:00
// Special case for code blocks within quote
if ( ( codeType = = Draw : : CodeTypeEnum : : CODE_BLOCK ) & & isQuote )
{
std : : istringstream iss ( text ) ;
std : : string line ;
// Add a quote for each new code line
2021-03-06 08:05:20 -08:00
while ( getline ( iss , line ) )
{
2021-03-05 15:58:11 -08:00
insertMarkupTextOnThread ( " <span font_desc= \" " + defaultFont . to_string ( ) + " \" foreground= \" blue \" > \uFF5C </span><span " + span + " > " + line + " </span> \n " ) ;
}
insertMarkupTextOnThread ( " <span font_desc= \" " + defaultFont . to_string ( ) + " \" foreground= \" blue \" > \uFF5C \n </span> " ) ;
}
2021-03-09 14:30:07 -08:00
// Special case for heading within quote
2021-03-12 13:44:48 -08:00
else if ( ( headingLevel > 0 ) & & isQuote )
{
insertMarkupTextOnThread ( " <span font_desc= \" " + defaultFont . to_string ( ) + " \" foreground= \" blue \" > \uFF5C </span><span " + span + " > " + text + " </span><span font_desc= \" " + defaultFont . to_string ( ) + " \" foreground= \" blue \" > \n \uFF5C \n </span> " ) ;
2021-03-09 14:30:07 -08:00
}
// Just insert text/heading the normal way
2021-03-05 15:58:11 -08:00
else
{
insertMarkupTextOnThread ( " <span " + span + " > " + text + " </span> " ) ;
}
2021-03-04 16:18:42 -08:00
}
2021-02-25 09:28:49 -08:00
}
2021-02-18 15:44:21 -08:00
/**
* Insert url link - thread safe
*/
2021-03-11 13:57:20 -08:00
void Draw : : insertLink ( const std : : string & text , const std : : string & url , const std : : string & urlFont )
2021-02-18 15:44:21 -08:00
{
DispatchData * data = g_new0 ( struct DispatchData , 1 ) ;
data - > buffer = buffer ;
2021-03-11 13:57:20 -08:00
data - > text = text ;
2021-02-18 15:44:21 -08:00
data - > url = url ;
2021-03-09 14:13:58 -08:00
data - > urlFont = urlFont ;
2021-02-18 15:44:21 -08:00
gdk_threads_add_idle ( ( GSourceFunc ) insertLinkIdle , data ) ;
2021-02-12 09:07:37 -08:00
}
2021-03-05 15:58:11 -08:00
/**
* Remove nr . chars from the end of the text buffer - thread safe
*/
void Draw : : truncateText ( int charsTruncated )
{
DispatchData * data = g_new0 ( struct DispatchData , 1 ) ;
data - > buffer = buffer ;
data - > charsTruncated = charsTruncated ;
gdk_threads_add_idle ( ( GSourceFunc ) truncateTextIdle , data ) ;
}
2021-03-06 08:05:20 -08:00
/**
2021-03-12 13:44:48 -08:00
* Encode text string ( eg . ampersand - character )
2021-03-11 13:57:20 -08:00
* @ param [ in / out ] string
2021-03-06 08:05:20 -08:00
*/
2021-03-12 13:44:48 -08:00
void Draw : : encodeText ( std : : string & string )
2021-03-06 08:05:20 -08:00
{
2021-03-11 13:57:20 -08:00
std : : string buffer ;
buffer . reserve ( string . size ( ) + 5 ) ;
2021-03-12 13:44:48 -08:00
for ( size_t pos = 0 ; pos ! = string . size ( ) ; + + pos )
{
switch ( string [ pos ] )
{
case ' & ' :
buffer . append ( " & " ) ;
break ;
default :
buffer . append ( & string [ pos ] , 1 ) ;
break ;
2021-03-11 13:57:20 -08:00
}
}
string . swap ( buffer ) ;
2021-03-06 08:05:20 -08:00
}
2021-02-18 15:44:21 -08:00
/******************************************************
* Helper functions below
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2021-02-11 14:20:23 -08:00
2021-02-18 15:44:21 -08:00
/**
* Insert markup pango text - thread safe
*/
void Draw : : insertMarkupTextOnThread ( const std : : string & text )
2021-02-11 14:20:23 -08:00
{
2021-02-12 09:07:37 -08:00
DispatchData * data = g_new0 ( struct DispatchData , 1 ) ;
data - > buffer = buffer ;
data - > text = text ;
2021-02-18 15:44:21 -08:00
gdk_threads_add_idle ( ( GSourceFunc ) insertTextIdle , data ) ;
2021-02-11 14:20:23 -08:00
}
2021-02-18 15:44:21 -08:00
/**
* Clear buffer - thread - safe
*/
void Draw : : clearOnThread ( )
2021-02-11 14:20:23 -08:00
{
2021-02-18 15:44:21 -08:00
gdk_threads_add_idle ( ( GSourceFunc ) clearBufferIdle , buffer ) ;
2021-02-12 09:07:37 -08:00
}
2021-03-02 15:04:48 -08:00
/* Looks at all tags covering the position (x, y) in the text view,
* and if one of them is a link , change the cursor to the " hands " cursor
* typically used by web browsers .
*/
void Draw : : changeCursor ( int x , int y )
{
Gtk : : TextBuffer : : iterator iter ;
bool hovering = false ;
get_iter_at_location ( iter , x , y ) ;
auto tags = iter . get_tags ( ) ;
for ( auto & tag : tags )
{
char * url = static_cast < char * > ( tag - > get_data ( " url " ) ) ;
if ( url ! = 0 & & ( strlen ( url ) > 0 ) )
{
// Link
hovering = true ;
break ;
}
}
if ( hovering ! = hovingOverLink )
{
hovingOverLink = hovering ;
auto window = get_window ( Gtk : : TextWindowType : : TEXT_WINDOW_TEXT ) ;
if ( hovingOverLink )
window - > set_cursor ( linkCursor ) ;
else
window - > set_cursor ( normalCursor ) ;
}
}
2021-02-12 14:30:39 -08:00
/**
2021-02-15 08:10:13 -08:00
* Insert text on Idle Call function
2021-02-12 14:30:39 -08:00
*/
2021-02-15 08:10:13 -08:00
gboolean Draw : : insertTextIdle ( struct DispatchData * data )
2021-02-12 09:07:37 -08:00
{
GtkTextIter end_iter ;
gtk_text_buffer_get_end_iter ( data - > buffer , & end_iter ) ;
gtk_text_buffer_insert_markup ( data - > buffer , & end_iter , data - > text . c_str ( ) , - 1 ) ;
g_free ( data ) ;
return FALSE ;
}
2021-02-15 08:10:13 -08:00
/**
* Insert link url on Idle Call function
*/
gboolean Draw : : insertLinkIdle ( struct DispatchData * data )
{
GtkTextIter end_iter ;
GtkTextTag * tag ;
gtk_text_buffer_get_end_iter ( data - > buffer , & end_iter ) ;
tag = gtk_text_buffer_create_tag ( data - > buffer , NULL ,
2021-03-09 14:13:58 -08:00
" font " , data - > urlFont . c_str ( ) ,
2021-02-23 15:35:51 -08:00
" foreground " , " #569cd6 " ,
2021-02-15 08:10:13 -08:00
" underline " , PANGO_UNDERLINE_SINGLE ,
NULL ) ;
g_object_set_data ( G_OBJECT ( tag ) , " url " , g_strdup ( data - > url . c_str ( ) ) ) ;
gtk_text_buffer_insert_with_tags ( data - > buffer , & end_iter , data - > text . c_str ( ) , - 1 , tag , NULL ) ;
g_free ( data ) ;
return FALSE ;
}
2021-03-05 15:58:11 -08:00
/**
* Truncate text from the end of the buffer
*/
gboolean Draw : : truncateTextIdle ( struct DispatchData * data )
{
GtkTextIter end_iter ;
gtk_text_buffer_get_end_iter ( data - > buffer , & end_iter ) ;
GtkTextIter begin_iter = end_iter ;
gtk_text_iter_backward_chars ( & begin_iter , data - > charsTruncated ) ;
gtk_text_buffer_delete ( data - > buffer , & begin_iter , & end_iter ) ;
g_free ( data ) ;
return FALSE ;
}
2021-02-12 14:30:39 -08:00
/**
2021-02-18 15:44:21 -08:00
* clearOnThread Text on Idle Call function
2021-02-12 14:30:39 -08:00
*/
2021-02-18 15:44:21 -08:00
gboolean Draw : : clearBufferIdle ( GtkTextBuffer * textBuffer )
2021-02-12 09:07:37 -08:00
{
GtkTextIter start_iter , end_iter ;
gtk_text_buffer_get_start_iter ( textBuffer , & start_iter ) ;
gtk_text_buffer_get_end_iter ( textBuffer , & end_iter ) ;
gtk_text_buffer_delete ( textBuffer , & start_iter , & end_iter ) ;
return FALSE ;
2021-02-11 14:20:23 -08:00
}
2021-02-12 09:07:37 -08:00
/**
2021-02-15 08:10:13 -08:00
* Convert number to roman numerals
2021-02-12 09:07:37 -08:00
*/
std : : string const Draw : : intToRoman ( int num )
2021-02-11 14:20:23 -08:00
{
2021-02-12 09:07:37 -08:00
static const int values [ ] = { 1000 , 900 , 500 , 400 , 100 , 90 , 50 , 40 , 10 , 9 , 5 , 4 , 1 } ;
static const std : : string numerals [ ] = { " M " , " CM " , " D " , " CD " , " C " , " XC " , " L " , " XL " , " X " , " IX " , " V " , " IV " , " I " } ;
std : : string res ;
for ( int i = 0 ; i < 13 ; + + i )
{
while ( num > = values [ i ] )
{
num - = values [ i ] ;
res + = numerals [ i ] ;
}
}
return res ;
2021-02-11 14:20:23 -08:00
}